【Win32】WM_MOUSEWHEELのlParamはクライアント座標じゃないよという話
そういえば、マップエディタを実装していてひとつハマったポイントがあったので備忘録として記しておく。
まあ、内容は記事タイトルがほぼすべて物語っているのだが…
WM_MOUSEWHEEL
画面の縮尺変更を実装するためにマウスホイールを使おうと思って WM_MOUSEWHEEL
イベントを購読してみたのだが、どうも挙動がおかしい。
なんというか、ウインドウを動かすと場所によっては WM_MOUSEWHEEL
が発生しないようで、マウスホイールが効かなくなってしまうのだ。
原因
調べたところ、lParam
の仕様によるものであることが分かった。
なんと、WM_MOUSEWHEEL
の lParam
には クライアント座標ではなくスクリーン座標が格納されている とのこと。
ええ…!? WM_MOUSEMOVE
とか、ほかのマウス系イベントは全部クライアント座標が格納されてたじゃないか。なんだこの罠は…
どうりでウインドウの位置によってマウスホイールが効いたり効かなかったりするわけだ。
解決方法
スクリーン座標をクライアント座標に変換する必要がある。以下はD言語で書いたもの。
auto pt = POINT(cast(short) LOWORD(lParam), cast(short) HIWORD(lParam)); ScreenToClient(hWnd, &pt); // ptがクライアント座標に書き換わる。
MAKEPOINTS
や GET_X_LPARAM
、GET_Y_LPARAM
のようなマクロは dmd v2.097.0 時点では移植されていないようなので、LOWORD
と HIWORD
を駆使して POINT
構造体を生成している。まあ、回りくどくなるだけで難しいことはしていないと思う。
環境によっては座標が負数になるとのことなので、cast(short)
を忘れずに。