【C++】長年の課題だったIME変換時のチラつきが解消した

C++
この記事は約3分で読めます。

テキストエディタを作り出した頃から IME の変換時に稀に発生するチラつきがどーしても解消できずに悩んでいました。今回ようやくチラつきゼロにできました。

まず、回避策を調査して知ったのですが、IMEには Input Method Manager (IMM32)とは別に Text Services Framework ってのもあるんですね。で、 Input Method Manager は ストアアプリでは利用できないと。

Windows8においてはIMMのAPIを用いて作成されたインプットメソッドはストアアプリに利用できずにシステムによってブロックされるため、TSFによる開発が必須となっている

https://ja.wikipedia.org/wiki/Text_Services_Framework

私が作っているテキストエディタで利用しているのは昔ながらの Input Method Manager (IMM32) の方です。でね、解消するのに色々調べてたりして、WM_ERASEBKGND を無視したりしたのですが、結局解決できませんでした。

IME系メッセージの発生順

そんなこんなでまずは、IME系メッセージの発生順から。Input Method Manager (IMM32)は 下記順にメッセージが送信されます。

1.開始時:WM_IME_STARTCOMPOSITION
2.入力中:WM_IME_COMPOSITION
3.終了時:WM_IME_ENDCOMPOSITION

IMEを起動すると WM_IME_STARTCOMPOSITION が届き、入力中は WM_IME_COMPOSITION が連続で届きます。そして確定すると WM_IME_ENDCOMPOSITION が届きます。 当然入力された文字はエディタに反映するのですが、WM_IME_ENDCOMPOSITION で DefWindowProc に渡す前に描画を完了させれば、チラつかなくなりました。具体的には下記な感じです。

// IMEで入力された文字を取得してテキスト編集
…
// 再描画
InvalidateRect(hWnd, NULL, FALSE);
// 強制描画
UpdateWindow(hWnd); // ←これを追加した

InvalidateRect の後の描画って実行タイミングは OS まかせですよね?ようはWM_IME_ENDCOMPOSITION のあとに、WM_PAINTが実行されたりするとチラつくようです。こんだけかい…。以前スクロールバーを実装していた時にあまりに重い色分けしているとスクロールが追いつかないことがあって、強制描画し経緯がありますが、その回避策と同じですね。気付かないもんですね。ずっとIME側の問題と思っていました。

解消するまで時間がめっちゃかかりましたが、チラゼロ最高ですね。今回、何回目の挑戦かわかりませんが、新たな試みとしてサクラエディタのソースも確認しています(私の環境ではサクラエディタはチラゼロです)

ただ、ソースみてデバッグ実行しても何故かわかりませんでした。ほとんど同じような実装になってるのになんでって?更に悩みました。

今度は SPY++ で発生しているメッセージを確認しました。サクラエディタの場合、IMEの変換後に WM_PAINT は発生せず、別タイミングでビットマップに描画しているようです(間違えていたらごめんなさい)

ここで、ようやく、ん?ですよね。自作エディタもメッセージ発生順を確認すると WM_IME_ENDCOMPOSITION の後に WM_PAINT が発生していました。そして、試しに、UpdateWindow を入れたら解決した次第です。

おわりに

他のチラつくエディタのメッセージを確認すると WM_IME_ENDCOMPOSITION の後に WM_PAINT が発生していましたので、原因の一つとして考えて間違いないかと思います。もし、IMEの変換のチラつきで悩んでいる方がいたら参考になればと思います(今の時代にいますかね?)

コメント

タイトルとURLをコピーしました