この記事の目的
この記事はオープンソースのWindows用スクリーンリーダーNVDAと、その派生プロダクトであるNVDA日本語版(nvdajp)の開発に関わりたい、ソースコードを読んで理解したい、という人に向けた情報です。
前提
NVDAにおけるIME対応の経緯
以下は「NVDA最新情報」のバージョン 2012.3 の記述の引用です。
NVDA は IME とテキストサービスによるアジア言語の文字入力を、全てのアプリケーションでサポートします。具体的には以下の機能です:
- 候補リストの項目の通知とナビゲーション
- コンポジション文字列の通知とナビゲーション
- 未変換文字列の通知
本家版の「アジア言語」対応は中国語(中国、台湾、香港)ユーザーの支援によって2012年の夏から秋にかけて行われました。私は当時、オブザーバーとしてミーティングにオンライン参加しました。日本語のカナ漢字変換(IME)において検証を行い、可能な範囲でフィードバックを行いました。
本家版が行った作業の多くは、日本語IME対応に役立つものであると判断したため、過去に「NVDA日本語化プロジェクト」などの活動で行ったカナ漢字変換対応の実験的な実装の多くを破棄し、あらためて開発を行いました。そして2013年5月にリリースした「NVDA日本語版 2013.1jp」で、日本語入力が実用的に使えるものとなりました。
NVDA日本語版が対応している日本語IMEは以下の通りです。
- Microsoft 日本語 IME (現在は Windows 7から11まで)
- ジャストシステム ATOK(2012年から現在まで ATOK Passport 版で検証)
2020年の Windows 10 バージョン 2004 以降で導入された新しい Microsoft IME では、従来の NVDA の IME 対応との互換性が失われました。NVDA 日本語版 2022.4jp では新しい Microsoft IME への対応を行いました。
候補リストの項目
入力メソッドの候補リストそのものの処理が NVDAObjects/inputComposition.py
に CandidateList および CandidateItem の各クラスにあります。これらの処理は、ふつうのリストやリスト項目との挙動の違いを定義しています。
CandidateItemBehavior の実装は NVDAObjects/behaviors.py
の CandidateItem クラスです。ここには getFormattedCandidateName など候補リスト項目に固有のメソッドがあります。
個別の入力メソッドの実装に依存した処理もあります。たとえば(2012年時点で本家が対象としていた)Microsoft IME は NVDAObjects/IAccessible/mscandui.py
で各バージョンについて振る舞いを細かく制御しています。
これらが行っているのは、たとえば以下のことです。
- 候補リストの項目は単に読み上げるのではなく「日曜日のニチ、本箱のホン」のように詳細読みを行う
- リストであること、リスト項目であること、などの冗長な情報の読み上げを行わない
mscandui のクラスと表示されたウィンドウの紐付けは NVDAObjects/IAccessible/__init__.py
で行います。
具体的には、findOverlayClasses() において windowClassName が 'mscandui' で始まる場合などの条件で mscandui.findExtraOverlayClasses
を実行します。
ATOK 対応は、NVDA日本語版で独自に追加した NVDAObjects/IAccessible/atok.py
で行い、前述の実装を真似て windowClassName の最初の5文字が ATOK2 または ATOK3 のときに ATOK 対応クラスと紐付けます。あと何年かしたら ATOK4 の場合を追加する必要がありますが。。
2020年以降の「新しい Microsoft IME」については、使われているAPIが UIA であるためappModules/windowsinternal_composableshell_experiences_textinput_inputapp.py
で NVDAObjects.UIA のサブクラスを実装しています。これはアプリケーションモジュールです。
このモジュールの AppModule で、ウィンドウと実装の紐付けを行います。
本家の実装が日本語IMEではうまく動かないため NVDA 日本語版は _jp.py
という別ファイルを追加しています。
コンポジション文字列と未変換文字列
コンポジション文字列の処理の一部は NVDAObjects/inputComposition.py
にあります。InputComposition クラスは NVDAObjects.window.Window のサブクラスです。
ですが、入力や変換のイベントや情報を取得するには、ウィンドウ以外の API が必要になります。キー入力そのもののイベント処理は入力メソッド対応には十分ではありません。
たとえば ATOK で(本来の)無変換キーの機能を使うと「半角英数」「ひらがなローマ字」のように入力モードの切り替えができます。これは NVDA が読み上げます。
この処理は handleInputConversionModeUpdate です。
このコードは nvdaHelper\remote\ime.cpp
の handleIMEConversionModeUpdate から実行されます。
IMN_SETCONVERSIONMODE は、Windows APIのInput Method Manager (IMM)に関連する通知メッセージです。このメッセージは、IME の変換モードが変更されたときに、対象のウィンドウに送信されます。
ime.cpp には、メッセージを NVDA が受け取るための初期設定と、受け取った場合に NVDAHelper のメソッドを呼び出す処理が書かれています。
TSF(Text Services Framework)はより新しい Windows API で、アプリケーションとIMEの連携を管理し、多言語入力とテキスト処理を効率的にサポートします。
TSF は以前から Microsoft IME で使われており、最近は ATOK も TSF に対応しています。
TSF 対応の処理は tsf.cpp に書かれています。
例えば nvdaControllerInternal_inputCompositionUpdate を呼び出す処理は imm.cpp と tsf.cpp で異なりますが、NVDAHelper.py では共通化されています。
さまざまな状況で inputComposition のコードを呼び出す処理も NVDAHelper.py に書かれています。
終わりに
ソースコードを読む準備ということで、細かいことには深入りしていません。また、実際には、個別のIMEやアプリのための「作り込み」はあちこちのモジュールに点在しています。
とはいえ、NVDA本家版は(主に中国語や韓国語ですが)アジア言語の文字入力に関して、これらの処理を、効率的に実装しています。
前述の handleInputConversionModeUpdate など、本家版にも日本語IMEの処理が入っています。
日本のスクリーンリーダー利用者が期待する「細かな機能の不足」や「期待する挙動の違い」についてNVDA日本語版が行った差分は大きくないのですが、本家版にマージできていないのが心残りです。
一方で、マージされたとしても、将来のリファクタリングや拡張で壊れてしまったときに、直し方を提案したり、どう壊れているかを伝える人が、日本にいないと問題だなと思い始めています。
この記事は NVDA 日本語版の開発を請け負っている株式会社シュアルタが、
AccessibleToolsLaboratory さんと一緒に行う勉強会の資料として @24motz が書きました。
これからも NVDA に関する技術情報をときどき整理したいと思っています。
文献
10年くらい前にこの作業をしたり人に説明したりするときに参考になった資料です。