このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
昨日の続き。MainProc の実装を行う。
MainProc は、スクリーンセーバー本体の
ウィンドウプロシージャーである。
scrnsave.lib に用意されている
DefScreenSaverProc 関数を使えば、
スクリーンセーバー固有の処理を任せることができるため、
MainProc 内部はそれほど複雑にならない。
========== Application.cpp ==========
#include "config.hpp"
#include "Application.hpp"
#pragma unmanaged
namespace Loafer {
namespace ScreenSaver {
namespace Host {
(…前略…)
LRESULT Application::MainProc(
HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam) {
try {
switch (message) {
case WM_CREATE:
dispatcher.Create(hwnd);
break; // 既定の処理にも回す
case WM_DESTROY:
dispatcher.Destroy(hwnd);
break; // 既定の処理にも回す
case WM_ERASEBKGND:
// ちらつき防止のため、背景を初期化しない
return 0;
case WM_PAINT:
dispatcher.Paint(hwnd);
return 0;
}
} catch (...) {
// あらゆる例外は握りつぶす
}
// 残りは既定の処理に任せる
return DefScreenSaverProc(hwnd, message, wParam, lParam);
}
}}} // Loafer::ScreenSaver::Host
========== end of Application.cpp ==========
WM_CREATE と WM_DESTROY は、
ウィンドウの構築と破棄に関するメッセージである。
これらメッセージを受け取って処理し、
Dispatcher に初期化と後始末を通知する。
WM_ERASEBKGND と WM_PAINT は描画処理の要である。
Windows の描画システムは 2 パスであり、
最初に背景を初期化するために WM_ERASEBKGND が呼ばれ、
続いて、本体の描画のために WM_PAINT が呼ばれる。
描画が 2 パスである理由は、描画処理の効率化のためだ。
一般的には WM_ERASEBKGND で背景全体を単色で塗りつぶし、
WM_PAINT で必要な部分だけ描画を行う。
WM_ERASEBKGND は、アプリケーションが処理しなくても
既定の処理である DefScreenSaverProc (DefWindowProc) が、
ウィンドウに関連付けられた背景ブラシを使って、
自動的に領域全体を塗りつぶしてくれる。
そのため、アプリケーションは、WM_PAINT を処理し、
最小限の描画コードを書くだけで済むのである。
ただ、この描画手順は、スクリーンセーバーのように
表現豊かで描画の頻度が高いアプリケーションには向かない。
理由は、WM_ERASEBKGND での描画(背景初期化)が、
画面に反映されるからである。
言い換えると、WM_ERASEBKGND が呼ばれたが、
WM_PAINT が呼ばれていない状態が表示されてしまうので、
描画頻度が高いとちらつきの原因になるのである。
例えば、Windows のメモ帳を起動し、
適当に文字を入力した後、
ウィンドウのサイズを色々変えてみる。
メモ帳の内部のテキストボックスは、
サイズが変わるたびに再描画されるのだが、
その際にちらつきが生じているのが分かると思う。
このちらつきを防ぐ簡単な方法は、
WM_ERASEBKGND では全く描画を行わず、
WM_PAINT で背景を含む全体を一度に描画することである。
そのためには、WM_ERASEBKGND の既定の処理を行わず、
アプリケーションが独自に WM_ERASEBKGND を受け取り、
DefScreenSaverProc (DefWindowProc) に渡すことなく、
直接戻り値として 0 を返せば良い。
戻り値 0 は、ここでは背景を初期化せず、
後々 WM_PAINT 等で背景の描画も行うことを意味している。
WM_PAINT では、Dispatcher に描画を指示しているが、
ここでは必ず全体を描画しなければならないことになる。
上記で処理したもの以外のメッセージは、
DefScreenSaverProc に渡されるため、
キーボードの押下やマウスの移動等は自動的に処理される。
では、このソースもコンパイラにかける。
cl /c /W4 /clr /FoApplication.obj Application.cpp
このソースも純粋な C++ だけだが、
リンクのことを考えて、/clr スイッチを使い、
#pragma unmanaged を指定することにした。
これで、Application.obj もできた。