Scrio 介紹 - 用途與架構 ======================= 作者: 遊手好閒的石頭成 (shirock@mail.educities.edu.tw) 日期: 2001/03/31 這是一個源自 FirebirdBBS 的畫面顯示函數。 可以到下列網址尋找套件: * http://fbtip.tsx.org 本計劃首頁的短址。 * http://residence.educities.edu.tw/fbtip/dl_software.shtml 套件存放區。 目 錄 -------------------------- 一、用途 二、架構 三、需要的系統函數 四、影嚮 scrio 運作的符號定義 五、後記 一、用途 ---------------- 它的用途,類似 cursor 這個 library ,原先是 FirebirdBBS 原始碼的 一部份,但是我將它獨立出來,並修改了許多地方,使各函數間的分工更 明確。 它不只能用在 BBS 上,也能用來開發 unix 上的文字介面程式。 我不保證功能比 cursor 或其他的更多或更好,但我確定的是,這套工具 ,很合我們這些玩 BBS 人的胃口。 因為它可以顯示中文(這是廢話);也 可以用ANSI控制碼去控制畫面,主要能控制的是文字顏色,游標控制碼被 我閹掉了(我幾乎實作了一個 ansi.sys);而且也很容易使用(至少我很習 慣)。 目前版本的主體,約完成於 1999年中,其後只有一些小修正,根據我的測 試 (我從前維護的某個 BBS 系統,它的畫面,就是用這library 處理的) ,這個 library 是相當穩定而可靠的,只有幾個構想,還沒有實作進去。 二、架構 ---------------- 整個 SCRIO 函數館,分成兩個部份,一個是 io.c,另一個是 screen.c. io.c 實作了如何與終端機溝通的函數,而 screen.c 則實作了如何存取 SCRIO 畫面緩衝區的函數。 ps. 原始基礎是來自 FirebirdBBS 2.31 版,在 FB 2.31 中,主要是分成 term.c, io.c, screen.c 三個檔案(還有一些則散佈在其他檔案)。 screen.c ─→ SCRIO 畫面緩衝區 │ refresh() or redoscr() │ └→ io.c ─→ oflush() ──→ 終端機 io.c 利用通用的終端機控制指令(vt-generic)控制終端機的動作,將終端 機輸入的資料放入輸入緩衝區(inbuf[512]),而將要輸出的資料放入輸出 緩衝區(outbuf[4096]) ,當輸出緩衝區堆滿資料時,會自動輸出資料給終 端機(oflush())。 screen.c 並不是直接與終端機溝通以進行畫面顯示的動作,而是建立一個 緩衝區(稱做 SCRIO 畫面緩衝區),存放畫面內容及變動記錄,一直到呼叫 redoscr() 或 refresh() 時,才呼叫 io.c 的函數,將畫面變動的內容輸 出到終端機上。 整個 SCRIO 的函數也分成兩組,一組是位於 io.c 中的簡單終端機控制函 數(大都是 do_??? 的名稱),另一組是位於 screen.c 中的畫面緩衝區控 制函數,提供較複雜但靈活的畫面處理動作。 程式開發者可以只使用簡單的終端機控制函數,也可以使用較靈活的畫面 緩衝區控制函數。 不論想如何使用,請務必先呼叫 init_term() 初始化終端機狀態後,才可 以使用 io.c 或 screen.c 裡的函數。 init_term() 勿重覆呼叫。 在呼叫 screen.c 裡的函數前,則務必先呼叫 initscr() 或 init_screen() 建立一個或以上的畫面緩衝區(理論上可以建立多個,但我 沒實作 switch 動作)。 init_screen() 不會自動呼叫 init_term() 。 當呼叫了 init_term() 尚未呼叫 init_screen()前,只可以使用 io.c 裡 的簡單終端機控制函數,呼叫 init_screen()後,則應只使用 screen.c 裡的畫面緩衝區控制函數,雖然仍然可以使用簡單終端機控制函數,但有 必要嗎? ps.使用簡單終端機控制函數輸出的內容,不會被記錄在畫面緩衝區中, 因此一但呼叫 redoscr() 重繪整個畫面後,就會不見。 三、需要的系統函數 ------------------ 因為終端機的控制方式在 unix 系統中,一向存在著不少差異,在編譯 SCRIO 前,需先確認系統中,是否存在下列函數。 * isatty() - SVID, X/OPEN, BSD43 * ioctl() - No single standard. 這是一個普遍存在的函數,但是用途太廣太雜, 沒有單一標準說明其各種使用方式跟參數。 * tcgetattr(), tcsetattr() - POSIX.1 * tgetnum() unknown... I could not find any information about termcap and tgetnum() in my Linux programming manual. 我原先是要把它廢掉的,不過還是 改成選擇性的方式,留了下來。 tgetnum() 並不具可攜性,例如我在 Solaris 2.x 系統上,甚至把 termcap 給 dump 出來,也找不到。 四、影嚮 scrio 運作的符號定義 ----------------------------- 下列是一些可以在產生 scrio library 時,用以調整編譯行為的 define * HAS_TERMCAP 預設是 0 (false) ,亦即不使用 libtermcap 。 SCRIO 中只有 tgetnum() 是屬於 termcap 裡的函數,但我在某些系統 所附的 termcap 中,找不到 tgetnum() (如 Solaris 2.x)。 由於 tgetnum() 在這裡的影嚮並不深遠,因此我預設是不使用。 tgetnum() 在這裡是用來取得使用者終端機畫面的長寬,如果確實想使 用這個功能的話,在產生 SCRIO 時,請加上 -DHAS_TERMCAP=1 的參數 ,別忘了也要加上 -ltermcap 告訴 linker 將 termcap 連結進來。 * TERMIOS Use POSIX style terminal interface. This is default. * SGTTY Use old style terminal interface. 即原 FB 2.31 裡的方式。 TERMIOS and SGTTY 不能一起使用, -DTERMIOS or -DSGTTY. * OBUF_FUNC_INLINE 預設是啟用。將跟 outbuf (輸出緩衝區)有關的函數線上化(inline)。 io.c的函數,在輸出時,是先將資料放在 outbuf 中,待存滿後或呼叫 oflush() 時,才呼叫 write() 實際輸出。 而跟 outbuf 有關的函數 有: do_output(), ostr(), ochar(), oflush()... 但是要先了解的一點是,C 語言實際上並沒有所謂的 inline function, 雖然 gcc 提供了 __inline__ 關鍵字,可以將函數線上化,但這只是提 供編譯時的參考依據,沒有強制性。 當編譯器不支援這功能,或是在最 佳化層級不夠,未包含函數線上化時,那些函數就只是個普通的 static function 。 * REGION_SCROLL_FORWARD_FIX 啟用局部上捲功能修正。 * REGION_SCROLL_REVERSE_FIX 啟用局部下捲功能修正。 * REGION_SCROLL_FIX 啟用兩種局部捲動功能修正。 預設是不啟用局部捲動的相關修正。 當啟用這種修正功能時, scrio 會自行重繪應被捲動的資料,而不是用 終端機的捲動控制碼處理。 亦即在啟用此修正功能後,效率會較差。 例如在 Win95/cygwin 的環境下,我就發現局部下捲功能有錯誤,每次 下捲後,第一行的資料都會被清除,而被捲動資料也未正常顯示。 此時 就需啟動 REGION_SCROLL_REVERSE_FIX 。 五、後記 ---------------- 其他的內容,散見於原始碼中。 至於要如何 make 呢,也請看 Makefile ,或者用 make help 也可以看見。 我只在 Linux (Slackware, RedHat), Freebsd (2.2.x, 3.x), Win95 中 實際使用過,所以在這幾個系統中,應該沒問題。 其中有個 sc-test.c ,是測試程式也是示範程式,煩請自行編譯。 -- The TIP Project 根基於 Firebird BBS 原始碼的改進計劃 Short URL: http://fbtip.tsx.org/ Group: http://groups.yahoo.com/list/firebird-tip Mailing list: firebird-tip@yahoogroups.com