![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ![]() | ||
![]() | ![]() | ![]() | ![]() | ![]() |
|
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
![]() | ![]() |
以下は、Mac OS X 10.4.11 上の Xcode 2.5 で説明しています。このサンプルはかなり膨大なので、複数 HTML で解説を行っているので注意してください。目次か最後のボタンで他の HTML へと移動します。
| 1 readme.txt の日本語訳 2 ビルド 3 サンプルを実際に動かしてみる 4 コンポーネントについての概説 5 プロジェクトの構成 6 BIM.r と BIMScript.h 7 共通ヘッダ BIMClientServer.h 8 メインエントリポイント 9 コンポーネントの初期化 10 セッションを開く 11 セッションを閉じる 12 コンポーネントについての情報を返す 13 サーバーの起動と初期化 |
バージョン 1.0
April 20, 2001
BasicInputMethod は、Mac OS X のためのテキストサービスコンポーネントとして実装された単純な入力メソッドです。これは、テキストサービスをクライアント/サーバーコンポーネントに分割したり、テキスト入力 Carbon イベントとプロセス間通信を送信・受信したりすることを含む、テキストサービスコンポーネントを書くことについての基本を実演します。入力メソッドとテキストサービスについてのさらなる情報は、<http://developer.apple.com/techpubs/macosx/Carbon/carbon.html> にあるテキストサービスマネージャー (Text Services Manager) のドキュメントを見てください。
BasicInputMethod は、Mac OS X のための単純な入力メソッドです。これは、2 つの部分を実装します。(1) テキストサービスコンポーネント(BasicInputMethod.component)と、(2) サーバーアプリケーション(BasicServer.app)です。サーバーアプリケーションは、テキストサービスコンポーネントバンドル内に収容され、そのため Finder から直接見えるわけではありません。
BasicInputMethod は、変換バッファ内にローマンキーボード入力を受けとります。リターンキーを押せば、テキストを「確定 (fix)」します。「Convert To Lowercase(小文字に変換)」と「Convert To Uppercase(大文字に変換)」メニューコマンドは、インラインホール (inline hole) 内のテキストを修正できるようにします。
サーバーは、キーボードパレットとイベント送信パレットを実装します。キーボードパレット上のキーをクリックすることで、対応するテキストが現在のインラインホールへと送られることになります。入力メソッドのテキストサービス(ペンシル)メニュー内の「Show Keyboard Palette(キーボードパレットを表示)」と「Hide Keyboard Palette(キーボードパレットを隠す)」は、キーボードパレットを表示または非表示にします。「Send Event(イベント送信)」は、クライアントアプリケーションにテキスト入力イベントを送って、結果を見ることができるようにします。現在のところ、OffsetToPos がサポートされる唯一のイベントです。
テキストサービスコンポーネントとサーバーとの間の通信は、CFMessagePort API を使います。
BasicInputMethod は、Project Builder プロジェクト(BasicInputMethod.pbproj)として実装されています。このプロジェクトは、テキストサービスコンポーネント自体(BasicInputMethod.component)に対して 1 つと、サーバーアプリケーション(BasicServer.app)に対して 1 つの 2 つのターゲットを含んでいます。単に BasicInputMethod ターゲットをビルドする必要があるだけです。BasicServer ターゲットは自動的にビルドされることになり、その出力が BasicInputMethod テキストサービスコンポーネントバンドルの SharedSupport ディレクトリ内にコピーされることになります。
テキストサービスコンポーネントを /Library/Components にコピーします。たとえば、
BasicInputMethod は、入力メニュー内に「Basic Input Method」として現れます。現在のところ、BasicInputMethod は自身を日本語入力メソッドとして認識し、そのため、入力メニュー内で(存在するなら)他の日本語入力メソッドの間に現れることになります。BIMScript.h において、テキスト、言語、基準リソース ID を変更することによって、日本語ではない言語に入力メソッドのスクリプトを変更できます。
テキスト入力機能をテストするには、テキストエディットのようなアプリケーションを実行し、入力メニューから「BasicInputMethod」を選択します。BasicInputMethod がアクティブにされたとき、すでに実行されていないなら、サーバープロセス(BasicServer.app)は自動的に起動されます。
| BIM.c | 入力メソッドの中核機能です。 |
| BIMComponent.c | テキストサービスに対する主要エントリポイントと、コンポーネントマネージャーに対する直接のインターフェースです。BIMComponentDispatch と、テキストサービスマネージャーによって呼び出されるセレクタにもとづく関数を収容しています。ほとんどの関数は、BIM.c 内の対応する関数をそのまま呼び出します。 |
| BIMInputEvents.c | (UpdateActiveInputArea を含む)Carbon イベントを使ってホストアプリケーションとテキストサービスマネージャーと対話するための関数です。 |
| BIMLaunchServer.c | サーバーを検出して起動するための関数です。 |
| BIMMessageReceive.c | CFMessagePort を初期化し、サーバーから入ってきたメッセージを処理するための関数です。 |
| BIMMessageSend.c | サーバーにメッセージを送るための関数です。 |
| BIM.r | 「thng」リソースとアイコンを含む、Basic Input Method に組み込まれるリソースです。 |
| BS.c | サーバーに対する主要エントリポイントです。 | |
| BSDebugPalette.c | 実装されていません。パレット実行中のデバッグを実装するための関数です。 | |
| BSKeyboardPalette.c | キーボードパレットを実装する関数です。 | |
| BSMessageReceive.c | サーバーの CFMessagePort を初期化し、クライアントテキストサービスコンポーネントを登録して追跡し、入ってきたメッセージを処理する関数です。 | |
| BSMessageSend.c | クライアントテキストサービスコンポーネントにメッセージを送る関数です。 | |
| BSPreferences.c | ユーザー環境設定を管理する関数です。環境設定は現在のところディスク上に保存されません。 | |
| BSSendEventPalette.c | イベント送信 (send event) パレットを管理する関数です。 |
| BIMClientServer.h | テキストサービスコンポーネントとサーバーアプリケーションの間で共有される定数とデータ構造です。 | |
| BIMScript.h | 入力メソッドがサポートするスクリプトと言語です。 |
さて、ダウンロードしたサンプルを Xcode でピルドしようとすると、たくさんの警告とエラーが出ます。Xcode 2.5 のデフォルト状態では、21 個の警告と 4 個のエラーです。3 年ほど前(2005年)にビルドしてみた時も、たくさん出た記憶があるので、だいぶ以前からこういう状態なのだと思われます。サンプルを試してみるためには、まずこれらのエラーをなんとかしなければなりません。まずエラーから見ていきます。
まず、BMInputEvents.c でいくつかの定数の未宣言エラーが出ています。これは定数名が変更されたためです。そのため、現在使われている定数名に修正しなければなりません。エラーは kConvertedText、kRawText、kCaretPosition に対して出ています。私自身は答えを知っていますが、初めてエラーに遭遇したものだとして対処法を考えてみましょう。まずエラーの場所に行きます。すると次のようなソースが見つかるでしょう。
この kConvertedText が見つからないわけです。ためしにコマンドダブルクリックしても反応しません。この定数名をコントロールクリックして、メニューから「書類内で選択されたテキストを検索」です。これはマニュアルで表示されている検索グループ内で選択テキストを検索します。もし「Reference Library」全体が検索グループに設定されていなければ、検索は失敗するでしょうから、検索グループを設定しなおして、もう一度行います。ちなみに「API リファレンス内で選択されたテキストを検索」では、検索結果はヒットしません。おそらく、廃止された定数名なので文章内容としては存在するものの索引付けされていなからでしょう。そうすると、Xcode 2.5 の場合、2 件がヒットします。最初のものはこのサンプル自体のソースなので意味がありません。2 番目は『Apple イベントマネージャーリファレンス』です。これを開いて kConvertedText を検索してみます。すると、kCaretPosition という定数が見つかります。
ここの「バージョンに関する注記」の部分に「Starting in Mac OS X v10.4, use the constants defined in “kTSMHiliteCaretPosition” in place of these constants.(Mac OS X v10.4 から、これらの定数のかわりに kTSMHiliteCaretPosition で定義された定数を使ってください。)」とあります。リンクをクリックすれば、その定数へと移動します。
したがって、これらの定数を現在使われているものに修正しなければなりません。kConvertedText を kTSMHiliteConvertedText へ、kRawText を kTSMHiliteRawText へ、kCaretPosition を kTSMHiliteCaretPosition へとそれぞれ変更します。こうしてピルドすると、1 個のエラーが残りました。
さて、このエラーはビルド結果ウインドウに表示され、JamToolExecution のエラーとなっています。どうやらコンパイル時ではなく、ビルドフェーズ中に何やら起こっているようです。このように、エラーと警告でエラーをダブルクリックした時に、ソースファイルに行かない場合は、たいていビルドやリンク時のエラーで、リンクしているライブラリやフレームワークの問題や、ビルドフェーズの設定などを考えてみるのが良いです。
これだけだと情報が少ないので、どのみち現在の OS X 用にビルドするのだし、ネイティブターゲットに変換してメッセージやターゲット設定をより見やすくしてみましょう。「プロジェクト」>「すべてのターゲットをネイティブにアップグレード」を選択して実行します。そして古いターゲットはいらないので除去します。そして再度ビルドしてみます。
ビルド結果ウインドウを見ると、最後にエラーが出ていますが、さっきと違う表示がなされています。「Running custom shell script(独自のシェルスクリプト)」のエラーで、mv コマンドがエラーを起こしています。このとき、プロジェクトを置いている場所によって、非常に長いパスが表示され、全部見れないかもしれないですが、マウスを上に置いたときに出るヘルプタグ内で改行して表示されるので、それを見るか、ビルドトランススクリプトを見てパスを確認します。どうやら、「build」フォルダ直下にビルド結果が出力されているものとしてスクリプトが書かれているようです。Xcode では、build の下にターゲットの構成(Development など)のフォルダが作られ、そこにビルドされたものが置かれます。このパスを修正してやる必要があるようです。
「グループとファイル」ペイン内の「ターゲット」の「BasicInputMethod」を開示すると、その下にいくつものビルドフェーズが並んでいますが、最後に「スクリプトを実行」というビルドフェーズがあります。これを選択して、「情報」ボタンを押すと、設定されているスクリプトが表示されます。
SYMROOT は、Xcode ヘルプによれば『ビルドプロダクト(未除去のプロダクトファイル)を格納する場所の最上位フォルダへのパス。デフォルトでは、「$(SRCROOT)/build」に設定されています。この場所は、「ビルド」環境設定パネルまたはプロジェクトインスペクタで設定できます。』となっています。このため、スクリプトがうまく実行されなかったわけです。そこで、スクリプト内の SYMROOT を BUILT_PRODUCTS_DIR に変更します。これは Xcode ヘルプによれば『プロジェクトに含まれるすべてのターゲットのビルドプロダクト(または、ビルドプロダクトへのシンボリックリンク)を格納するためのフォルダへのパス。デフォルトでは、「$(SYMROOT)/$(CONFIGURATION)」です。スクリプトを実行するビルドフェーズでこのビルド設定を使用して、各ターゲットのビルドプロダクトを参照できます。これらのプロダクトがファイルシステム内の異なる場所に存在する場合でも、1つのインストールビルドとして参照できます。このビルド設定は読み込み専用です。変更しないでください。』で、これを使えば、Development フォルダ内のビルドされたものを扱ってくれるはずです。
本来はこれでうまくいくはずですが、デバッグ中に InputServer ターゲット内のファイルを修正し、再ビルドした時にエラーが出たりします。mv コマンドはデフォルトで既存のファイルを上書きするはずですが、うまく上書きできない場合があるようです。-f オプションも効かないので、ディレクトリを削除して、それから移動します。また、クリーニング直後ではディレクトリが存在しないのでエラーが出るので、どうせ .app はファイルではなくディレクトリなんで、それを作ってから削除という手順にすれば大丈夫でしょう。他の方法もあるかもしれませんが、とりあえず思いついたこの方法でいきます。
これでエラーはすべて除去され、警告は残るもののビルドは完了します。上の修正前のスクリプトでは、ビルドされたコンポーネント内にフォルダを作成していました。「build」フォルダ内を見ると、直下に BasicInputMethod.componet が作成されています。パッケージ内を見ても、フォルダが存在するだけです。これは、先の修正前のスクリプトの最初のディレクトリ作成コマンドで作られた空のディレクトリです。いらないので除去してしまいましょう。
警告は放っておいてもビルドには影響ありませんが、動作に問題が出ることもあるため、いちおうチェックしておきます。このサンプルの場合は、深刻なものはありません。
まず、一番多いタイプが文字列リテラルに関するものです。BSKeyboardPalette.c は警告 2 つなので、これから見ていきます。
赤で表示されているのが警告が出た行です。最初の行は、「pointer targets in initialization differ in signedness(初期化におけるポインタ対象の符号が違う)」という警告です。ここで文字列リテラルが代入されていますが、Xcode の GCC では、通常、文字列リテラルは char になります。それが unsined char に代入されているので警告が出ているわけです。無視してもかまいませんが、修正しておきます。次の行も同様です。CFStringCreateWithCString の部分をコマンドダブルクリックして宣言を見てみると、2 番目の引数が const char * として宣言されていることがわかります。これは、最初の title 変数の型を単なる char になおします。これで再ビルドしてみると、このファイルで警告が出ないことがわかります。これと同様の修正を BSSendEventPalette.c と BIMComponent.c で行いますが、ここでは細かい指示は書きません。BIMComponent.c における BIMLog に関するエラーのようにコマンドダブルクリックを使って、BIMLog 関数の引数型を宣言と定義の 2 か所修正したりするなど、警告が出ている場所ではなく、他の場所を修正する必要があることに注意してください。
これで、警告もエラーもなく、ビルドが完了しただろうと思います。それではビルドされた結果を実際に動かしてみて、サンプルを調べてみることにしましょう。
さて、readme.txt の指示にあるように、Development フォルダ内のビルドされたコンポーネントを、「ホーム」>「ライブラリ」>「Components」へとコピーして、ログアウトログインもしくは再起動します。そのシステム環境設定の「言語環境」>「入力メニュー」へと移動します。キーボードと入力メソッドのリスト内に BasicInputMethod が表示されているはずです。
![]() |
ここで有効であればメニューバーの入力メニュー内に BasicInputMethod が表示され、利用可能になります。
![]() |
テキストエディットなどのテキスト入力アプリケーションを起動します。そして、BasicInputMethod を選びます。画面上にキーボードパレットが表示されます。
![]() |
キーボードパレットから A、キーボードから小文字で b などと入力をしてみましょう。それから、入力メニューから「Convert To Lowercase(小文字に変換)」を選ぶと、すべて小文字に変換されます。また「Convert To Uppercase(大文字に変換)」を選ぶと、すべて大文字に変換されます。最後にリターンキーを押すとテキストが確定され、変換中を示していた下線が消えます。
![]() | ![]() | ![]() | ![]() |
メニューから「Show Send Event Palette(イベント送信パレットを表示)」を選ぶと、イベント送信パレットが表示されます。ボタンを押しても何も起こりません。コードを調べたときに、このボタンの動作について説明することになるでしょう。
![]() |
当然のことながら、パレット表示・隠すメニューを選べば、パレットは表示されたり隠されたりします。パレットのクローズボックスを押して閉じたとき、すぐに入力メニューを選んでも、メニュー項目の表示が正しく変更されていないことに注意してください。これは、メニューを経由しないでパレットを閉じた場合には、正しい処理がなされていないためです。
さて、サンプルの機能は以上のようなものです。これだけを見ても、何が行われているかわからないかもしれません。これから、コードを調べながら、テキストサービスコンポーネントがどのように動作するのかを探っていきます。最初に、「コンポーネント」とは何か、どういう風に動作しているのかについて概説することにします。
このサンプルを理解するには、コンポーネントとテキストサービスマネージャーについて理解している必要があります。とはいえ、入力メソッドを自作するために、すべてを理解している必要はありませんので心配ありません。テキストサービスマネージャーについては当サイトで翻訳済の『Carbon におけるテキスト入力とテキストサービスマネージャーの理解』(Carbon/Conceptual/UnderstandTextInput_TSM/)と『Text Services Manager リファレンス』(Carbon/Reference/Text_Services_Manager/)を見てください。
コンポーネントマネージャーについては、あまり良い説明がありません。特に Mac OS X 以降のものは、変更された部分の記事ばかりで、包括的な説明は Mac OS 9 以前の古い『インサイドマック』を見なければなりません。現在のリファレンスライブラリでは、QuickTime の記事として当サイトでは未訳の『Introduction to Component Manager for QuickTime』(QuickTime/Conceptual/ComponentMgr)と『Component Manager Reference』(Carbon/Reference/Component_Manager)ぐらいです。あとは、ADC の Technical Note や Q & A にある変更部分などについての注記を読むしかありません。
Mac OS 9 のコンポーネントと違う点として、最も注意したほうがいい所は、Mac OS X におけるコンポーネントは、各アプリケーションのメモリ領域内に作られるこということです。OS 9 以前では、コンポーネントはシステムが管理する共有メモリ内に作られ、複数のアプリケーションで共通に使われていました。この形だと、コンポーネントはただ 1 つしか作られず、メモリが節約できます。しかし、大きな欠点として、あるアプリケーションがクラシュ等の問題を起こしたとき、その時に使用中の入力メソッド等のコンポーネントも影響を受け、結果としてクラッシュやエラーがシステムや他のアプリケーションへと波及してしまいます。
Mac OS X では、システムのメモリ保護機能のため、古い形でのコンポーネントは無理があります。そのため、各アプリケーションのメモリ領域内にコンポーネントが作成されることになります。このため、アプリケーションごとに別々のインスタンスがあることになるので、古いコンポーネントコードを OS X 用に移植する場合は、これに気をつけなければなりません。特に、インサイドマック等の古い情報を参照してコンポーネントを理解している場合、この違いに注意してください。
OS 9 の頃は、コンポーネントは共有されていたので、共通する機能はコンポーネント自体が持っていても良かったのですが、OS X からは、共通機能は別の形で実現したほうがよくなっています。もちろん、すべてをコンポーネントに入れてもかまいません。この場合、負荷が大きくなるだけのことです。
BasicInputMethod やその他の入力メソッドは、たいていクライアント(利用者)・サーバー(供給者)型のモデルを使って、これを解決しています。各アプリケーションごとにコンポーネントのインスタンスがあり、それとは別にサーバーアプリケーションがただ 1 つ実行されています。各コンポーネントはサーバーに対してプロセス間通信を使ってやりとりし、共通機能を実現することになります。他には、この形ではなく、外部ファイルなどを利用するやりとりが考えられます。しかし、更新などのタイミングを考えると、やはり何らかの形でプロセス間通信ないし通知は必要になると考えられます。プロセス間の通知は可能ですが、けっこうコストが高いので、結局はクライアント・サーバー型の形にするのが無難かもしれません。他にはライブラリなどを使う方法も考えられます。いずれにしても、共通部分を何らかの形で外部に出したほうがいいでしょう。
上述したように、BasicInputMethod はクライアント・サーバー型の構造を持っています。そのため、プロジェクトファイル内に 2 つのターゲットがあることに注意してください。それを踏まえた上で、プロジェクトファイルを見ていきます。
プロジェクトファイルは、おおまかに次のような構造になっています。
![]() |
最初は各アプリケーションごとのコンポーネントを実装する BasicInputMethod 関連ファイル、そしてサーバーアプリケーションである BasicServer 関連ファイル、そして両方に共通のヘッダを収容している Shared Headers があります。つぎに外部フレームワークがあり、その後、情報プロパティリストファイルがあります。この情報プロパティリストは、「ネイティブターゲットにアップグレード」を行ったとき、- がたくさん入った長いファイル名にされています。ここでは、それを除去して短いファイル名にしてあります。ターゲットが両方に対して 1 つずつあることに注意してください。
まず、BasicInputMethod グループ内はつぎのようになっています。
![]() |
各ファイルは、readme.txt の最後のリストで説明されています。BIMScript.h は、readmee.txt では、共通ヘッダとして紹介されていることに注意してください。BasicBasicServer グループ内はつぎのようになっています。
![]() |
これらも readme.txt の最後のリストで説明されています。共通ヘッダは以下のようになっています。
![]() |
これも readme.txt の最後のリストで説明されています。
各ターゲットも見ておきましょう。まず、BasicInput メソッドです。
![]() |
最初に BasicServer が組み込まれていることに注意してください。これにより、BasicInputMethod のビルドのたびに BasicServer もビルドされます。ビルドされた BasicServer は、「2.1 ビルドするための修正」で説明するように、最後の「スクリプトを実行」ビルドフェーズでコンポーネント内に移動されます。「ResourceManager にリソースをビルド」で BIM.r が指定されていて、リソースが作成されることに注意してください。つぎは BasicServer です。
![]() |
最後に「ResourceManager にリソースをビルド」がありますが、これにはファイルが設定されておらず、上の関連ファイルを見てわかるようにイベント送信パレットは nib ファイルにもとづいていることに注意してください。
Info-BasicInputMethod.plist を見みましょう。注目するのは以下の項目です。
CFBundlePackageType は、thng となっています。これはタイプとクリエータのタイプにあたるものです。コンポーネントなので thng が指定されています。この thng は、BIM.r で thng リソースとしても定義されていて、BIM.r のところで説明します。
つぎの CSResourcesFileMapped ですが、これについては『ファイルシステムパフォーマンスガイドライン』の「ファイルのメモリへの対応付け」の「リソースファイルの対応付け」で説明されています。これを使うことで、CFBundle 関数でリソースファイルを読み込むとき、データフォークのみのファイルの内容をメモリ内に読み込んで、それをリソースとして対応付けます。
さて、つぎに Info-BasicServer.plist を見てみましょう。
この NSUIElement は、現在では廃止されていて、LSUIElement という風に名前が変わっていることに注意してください。Mac OS X v10.4.11 段階ではいちおう動作はしますが、変更しておいたほうがいいでしょう。『実行時設定ガイドライン』によれば、これが 1 に設定されていると、ドックや強制終了ウインドウに現れず、バッググラウンドとして動作するものの、必要があればユーザーインターフェースを提示できる、とあります。このサーバーアプリケーションは、BasicInputServer におけるパレットを実装しているので、これが指定されています。
これと似たものに、LSBackgroundOnly がありますが、この場合は、ユーザーインターフェースを提示することができません。そのため共通パレットを実装するためには、上の LSUIElement にする必要があります。もしサーバーにユーザーインターフェースがないなら、LSBackgroundOnly でかまいません。
さて、通常のアプリケーションサンプルの説明では、プロジェクトファイルの後で nib ファイル等のリソースを説明し、その後で各ソースへと移っていました。このプロジェクトは、ターゲットが 2 つあり、しかもプロセス間通信を使って、相互にやりとりを行っています。そのため、別々に分けて説明するとわかりにくくなります。
そこで、まずコンポーネントの中心となる thng と dlle リソースを定義している BIM.r をとりあげ、コンポーネントの基本的な部分を調べてみます。これには BIMScript.h がインクルードされているので、同時にこのヘッダも説明します。そのつぎに共通ヘッダを見ておきます。それから、コンポーネントの基本的な実装を調べて、テキストサービスコンポーネントとしての基本機能を調べます。そして、サーバーアプリケーションの基本的な実装を調べた後で、両方のプロセス間通信に関わる部分を説明します。プロセス間通信がわかったところで、それを使って、各機能をどう実装しているのかを見ていきます。(以下、次ファイルへと続く)
![]() | ![]() |
![]() |
管理人:神吉 秀典 E-mail: