A3サイズの升目帖(旧)

ゲーム制作やプログラミングに関する雑記

【Win32】WM_MOUSEWHEELのlParamはクライアント座標じゃないよという話

Windowsのロゴマーク


そういえば、マップエディタを実装していてひとつハマったポイントがあったので備忘録として記しておく。

まあ、内容は記事タイトルがほぼすべて物語っているのだが…


WM_MOUSEWHEEL

画面の縮尺変更を実装するためにマウスホイールを使おうと思って WM_MOUSEWHEEL イベントを購読してみたのだが、どうも挙動がおかしい。

なんというか、ウインドウを動かすと場所によっては WM_MOUSEWHEEL が発生しないようで、マウスホイールが効かなくなってしまうのだ。


原因

調べたところ、lParam の仕様によるものであることが分かった。

なんと、WM_MOUSEWHEELlParam には クライアント座標ではなくスクリーン座標が格納されている とのこと。

ええ…!? WM_MOUSEMOVE とか、ほかのマウス系イベントは全部クライアント座標が格納されてたじゃないか。なんだこの罠は…

どうりでウインドウの位置によってマウスホイールが効いたり効かなかったりするわけだ。


解決方法

スクリーン座標をクライアント座標に変換する必要がある。以下はD言語で書いたもの。

auto pt = POINT(cast(short) LOWORD(lParam), cast(short) HIWORD(lParam));
ScreenToClient(hWnd, &pt); // ptがクライアント座標に書き換わる。

MAKEPOINTSGET_X_LPARAMGET_Y_LPARAM のようなマクロは dmd v2.097.0 時点では移植されていないようなので、LOWORDHIWORD を駆使して POINT 構造体を生成している。まあ、回りくどくなるだけで難しいことはしていないと思う。

環境によっては座標が負数になるとのことなので、cast(short) を忘れずに。


参考文献