#osxchat blog

2005/07/23

OS X 輸入法架構對 OpenVanilla 開發所造成的困擾(對 zonble blog 的補充)

作者: lukhnos

zonble 一個晚上寫了兩篇「怎樣在 OS X 下寫一套輸入法」的文章,文章的範例之一是 OpenVanilla 計劃之初就建立的 CarbonInputMethod (以下簡稱 CarbonIM)這套程式庫。OpenVanilla 因為有了 CarbonIM 的基礎,才得以建立後續的動態載入架構。

本質上,CarbonIM 只是重新包裝了 Apple 的 BasicInputMethod 這套範例,僅僅改用 C++ 撰寫。然而因為我們在開發 0.6.x 的時候,對於 Apple 的 text service component 還不是完全瞭解,因此 0.6.x 時期許多的 bug (例如 GUI 版的 vi 無法使用),其實都源自於 CarbonIM。

Apple 的 text service component 是一個很奇怪的東西,嚴格說來完全是 OS 9 時代的遺跡。要理解為什麼在 OS X 上開發輸入法有這麼多奇奇怪怪的彎路要走,我們必須回到「OS X 的輸入法架構是 pre-OS X 時代的遺跡」這件事。

首先是包裝方式。輸入法模組是 OS X 裡極少數還需要靠檔案裡的 resource fork 來告訴作業系統這個模組功用為何的檔案群。也就是說,明明 OS X 的程式庫或應用程式,都是以 .bundle 或 .app 形式包裝,並藉由包裝裡的 Contents/Info.plist 這個檔案來描述包裝的類型及各項參數,但是輸入法模組得靠原始碼的資源檔 (.r) 裡的奇異檔頭,以及檔案的四字型別(存在於 resource fork 中,例如輸入法模組必需是 "THNG") ,OS X 才會認得。可以看得出來 Apple 從 OS X 10.3 開始,有陸陸續續將一些輸入法功能參數,放進 Info.plist 中(這點從 10.3 的日文輸入法,以及 10.4 的繁體中文輸入法模組看得出來),但是輸入法模組仍然需要靠 resource fork 裡的資料才能讓 OS X 知道這是一個輸入法,而這其實是和 OS X 整體設計相當格格不入的事。

其次是檔名,以OS X 版的酷音輸入法來說,酷音輸入法的檔名是 ChewingOSX.component ,但是自從 ProjectBuilder (pre OS X 10.3 時代的開發工具)改名為 Xcode 後,編製 .component 的選項就消失了。Apple 統一將所有可動態載入的的程式庫改名叫 .bundle。然而系統還是認得 .component 的檔名的。而且至今仍然有很多 .component 存在於 Tiger 中。

再來是 .component 的功用。所有的 .component 都應該放在 /System/Library/Components/、/Library/Components 以及 ~/Library/Components 這三個目錄中。很奇異的是,在 OS X 時代其實已經很少聽說有什麼東西是需要編成 .component 形式的了。我相信大多數人的 /Library/Components 裡應該只剩一種類型的檔案:輸入法模組(MacUIM、酷音、OV 等等)。

最後是 text service component 在記憶體裡存在的方式。在 OS 9 時代,text service component 在記憶體中只有一份拷貝(精準的說法是只有一個 instance)。每一個使用到輸入法的視窗都會建立一個 text service "session"。但這件事到了 OS X 變得有點複雜:首先,每一個應用程式都有一份 text service component 的 instance (理由是,text service component 是動態程式庫),而每一個應用程式又再從該 instance 中建立不同的輸入法 session。

奇怪的地方就在於:既然每一個應用程式都有一份 text service component 的 instance,那麼理論上要 debug 輸入法應該很容易。如果輸入法有某段程式爛掉了,把現行的應用程式關掉,把錯除掉、重新編譯、換上新的 text service component,再開一個新的應用程式就好了。但是 OS X 的 text service component 不是這樣運作的──OS X 似乎會在記憶體中留一份拷貝,而後應用程式載入,要動態繫結時,似乎會從這份記憶體的拷貝(而不是更改過的模組檔案)再拷一份(建立一個 instance)出來。

這也就是為什麼在 OS X 上 debug 輸入法是一件酷刑:只要有一行寫錯,就必須 logout, login,重新載入應用程式。有問題?logout, login、重新載入應用程式......

OpenVanilla 0.6.x 的第一步,就是把「OS X 的 text service component 程式碼」和「個別的輸入法程式碼」分開,把後者(例如倉頡模組、注音模組、酷音模組)變成 UNIX 的動態程式庫──OS X 的延伸名為 .dylib,在 Linux 上為 .so──這樣一來如果個別輸入法的程式出錯,就不需要再 logout, login 了,只要關掉現存的應用程式(例如關掉 TextEdit.app),然後換上新的模組(例如 sudo cp ~/OVIMChewing.dylib /Library/OpenVanilla/0.6.3/),再開一次 TextEdit.app 就成了。

但是這還是沒有解決 text service component 的除錯問題。一直到了 OpenVanilla 0.7.x ,因為一個架構的改變,而使得連 text service component 也可以像 0.6.x 的個別輸入法模組一樣修正。要做到這個架構改變,就必須把 CarbonInputMethod 推翻掉,幾乎要從頭重寫每一行程式。也因為這樣,從 0.6.x 推進到 0.7.x,花了四個月才完成。

標籤: , ,

1 篇留言:

  • 不是針對這一篇,而是關於 Open Vanilla 輸入法的一個消息。我在閱讀 nikolaoschen 的 blog 時得知他用 OV 為基礎,開發了古希臘輸入法。在 OV home page 上沒有列出。應該宣傳一下。

    http://chchen74.blogspot.com/2005/07/ov_28.html

    作者: Blogger Q3Q 發表時間: 8/13/2005 12:13:00 下午  

張貼留言

逆向鍊結:

建立連結

? 回前頁