怎樣在 Mac OS X 下寫一套輸入法?(一)

Glider 最近推出了 QIM的體驗版本。QIM 是一套在 Mac OS X 環境下的簡體中文拼音輸入法,而在 glider 開始開發 QIM 的時候,曾經有向 OpenVanilla 的開發團隊詢問有沒有關於在 Mac OS X 下開發輸入法的相關文件,而那時候居然答不出來,一方面是因為蘋果官方本身所提供的文件就不多,甚至有許多與作業系統溝通的方式,都還找不到文件,在開發 OpenVanilla 的時候,也都是在缺乏文件的狀況下摸索,而在摸索的同時,自己卻也沒有留下多少文件。想想這樣還是不行,還是該來弄一點文件。

要在 Mac OS X 下開發一套輸入法,可以有許多選擇,一種就是開發成 OpenVanilla 的模組: OpenVanilla 計畫的目標,就在於簡化開發輸入法的難度,例如各種輸入法大概都會用到的候選字視窗、文字緩衝區(buffer)、提示訊息的行為,資料的編碼格式轉換等等, lukhnos 都已經包裝成了物件,所以只要決定這些物件在什麼時候該做什麼事情,就可以弄一套輸入法模組出來,而在 OpenVanilla 中,已經有一套「通用輸入法模組」,只要準備好一個純文字檔案,寫好按鍵與字碼之間的對應,就可以成為一套輸入法。

而如果說 OpenVanilla 是一套輸入法,其實是很含糊的說法,因為 OpenVanilla 包含非常多的部份,包括做為系統元件(System Component)的載入器、個別的輸入法模組、文字過濾(Filter)處理模組、偏好設定應用程式等。而每個部份都是分散在各地的團隊成員分別負責,所以,有些不是我所負責的部份,我也不清楚怎麼運作,我也就只能夠分享我所知道的部份。

如果不打算使用 OpenVanilla 所提供的物件與工具,而直接打算寫一套會出現在「系統偏好設定」(System Preference)應用程式中「國際設定」(International)控制台的輸入法,像 glider 正在寫的 QIM 一樣,那就是必須從系統元件開始寫起了。在 Mac OS X 環境下,輸入法被視為是提供文字服務(T ext Service)的系統元件,系統元件程式會放在幾個地方,包括在根目錄、個人目錄、以及「系統」目錄下的「Library/Components」目錄下,系統元件是以「component」或「bundle」作為延伸檔名的Bundle目錄,例如,在「系統」目錄下的「Library/Components」目錄裡,可以找到TCIM.component以及SCIM.component,分別就是繁體中文以及簡體中文輸入法這兩個系統元件。

關於系統元件,可以參見蘋果開發者網站上的說明:Introduction to Component Manager Reference,另外,其實就算在 Mac OS X下,在輸入法方面其實有很大的部份都與之前的作業系統相同,也請先參考 Mac OS 8 and 9 的Text Services Manager,這篇可說是官方對於輸入法最為詳盡的技術文件。

要開始寫這樣一個系統元件的方法,就是從已經可以成功編譯的範例程式開始改寫,當然,您需要先安裝蘋果的開發工具 Xcode,這邊就不贅述了。蘋果開發者網站提供了一個輸入法範例BIM(Basic Input Method),目前許多 Mac OS X 上的輸入法專案,例如之前的香草輸入法、酷音輸入法以及 MacUIM 這套日文輸入法,都是從 BIM 上開始的。而 lukhnos 之前為了重構 OpenVanilla,重新整理了一個輸入法範例,叫做 Carbon Input Method(以下簡稱 CIM),將 BIM 大幅簡化,對於寫一個輸入法元件來說,也變得比較容易一些。您可以使用 svn 取得 CIM:http://svn.openfoundry.org/carbonim/branches/0.1/,以下也是以 CIM 作為範例解釋。

在一個輸入法系統元件中,主要包括兩個部份,第一個部份是與系統溝通的資源(Resource),延伸檔名為「.r」,其他就是程式的部份,依據程式寫作時使用的不同語言,延伸檔名為「.c」、「.cpp」、「.m」 或「.mm」等,需要注意的是,在 Mac OS X 上,輸入法的核心部份必須以 Carbon 寫作,但是可以使用 Carbon 呼叫 Cocoa 程式。

CIM 範例中的資源檔案是 CIM.r。打開這個檔案,我們會先看到以下這一段:

resource 'thng' (128)
{
'tsvc', /* 系統元件類別,一定要是 tsvc,代表文字服務(Text Service) */
'inpm', /* 系統元件次類別,一定要是 inpm,代表輸入法(Input Method) */
cimVendorCode, /* 廠商名稱 */
0x8000+cimScript*0x100+cimLanguage, /* 輸入法的語系 */
kAnyComponentFlagsMask, /* 一定要設定成這個數值 */
'dlle', cimBaseResourceID, /* 其他要使用的資源 */
'STR ', cimBaseResourceID,
'STR ', cimBaseResourceID,
'ICON', cimBaseResourceID,
0x00010000,
componentHasMultiplePlatforms,
15872+cimScript*0x200,
{
0x8000+cimScript*0x100+cimLanguage,
'dlle', cimBaseResourceID, 1000
}
};

其中,cimVendorCode、cimLanguage、cimBaseResourceID 等變數,都定義在 CIMconst.h 裡,cimVendorCode 是用來識別廠商名稱的,必須是四個小寫英文字母,例如原本定義成 ‘lkns’,就是 lukhnos 的簡稱。另外需要注意的就是輸入法語系設定的部份,格式如範例中所寫的,必須是

「0x8000 + script code * 0x100 + language code」

script code 的資訊可在這篇蘋果開發者文件中取得,Language Code則在,以繁體中文來說,Script code是「smTradChinese」,Language Code是「langTradChinese」,簡體中文則分別是「smSimpChinese」以及「langSimpChinese」。

接下來我們要看的,就是為「其他要使用的資源」部份。這邊的其他資源包括選單上的文字、輸入法顯示在使用者介面上的名稱,以及圖示。而其實在 Mac OS X 的架構下,這些東西都可以不用塞到資源檔案當中,而且在 Mac OS X 使用多國語文地方化(localization)架構下,如果將各種文字都寫死在資源裡頭,其實就枉費了 Mac OS X 的地方化架構的用意;也就是,Mac OS X會在每一個應用程式或系統元件 Bundle 的目錄中,擺放許多延伸檔名為「.lproj」的目錄,例如英文是「English.lrpoj」、繁體中文為「zh_TW.lproj」,而簡體中文則是「zh_CN.lproj」等,在這些目錄下,可以擺放各國語文翻譯的文字檔案,然後同一個應用程式,在不同的語系下,就可以以不同的語言顯示應用程式名稱以及選單文字,而至於怎樣以地方化的方式建立選單文字,容後再述。

而在 Mac OS X 的環境下,其實可以用一個獨立的tif圖檔,當作輸入法的圖示,在 10.3 系統內建的日文輸入法,以及 10.4 內建的簡體與繁體中文輸入法,都是如此,但是怎麼做?目前卻一直看不到相關的文件。所以在 OpenVanilla 中,仍然是將圖示放在資源檔案中。

在 CIM.r 當中,可以看到定義了 data ‘kcs#’、data ‘kcs4’、data ‘kcs8’,是在不同顏色數量下所顯示的不同圖示,但是其實現在所有的使用者,幾乎都是使用全彩的環境,所以大概只需要將圖示的資料放在 kcs8 裡頭即可,在這些 data 中,定義的就是一張點陣圖的每一個點陣的顏色,而其實要自己慢慢用手寫出一張點陣圖的資料,實在太辛苦了,所以還是得用一些工具。

輸入法的圖示是一張 32×32 pixel 的點陣圖,如果使用 Photoshop 繪製圖示,可以用Icon Builder這類的工具,將 Photoshop 圖片輸出成圖示,記得要選擇 Classic 的格式,也就是延伸檔案為「.rsrc」的那種,而不是選「.icns」的那種,這樣便可以產生資源檔案格式的圖示檔案。接著,我們便使用 Xcode 當中的一個工具—DeRez,這個工具位在「/Developer/Tools」,是一個命令列(CLI)的程式,只要用這個工具指定你所產生的圖示檔案的路徑與檔名,便可以得到當中的訊息。例如在桌面上放了一個叫做myicon.rsrc的圖示,在終端機中,便是下以下指令:

/Developer/Tools/DeRez ~/Desktop/myicon.rsrc > ~/Desktop/myicon.txt

如此一來,桌面上就會出現一個叫做 myicon.txt 的文字檔案,只要把裡頭的內容,貼到 CIM.r 裡頭就可以了。