マウスキャプチャー


ボタンの解放を監視する

筆者はカードキャプターの方が好きですが(死)
今回は、マウスキャプチャーと呼ばれるマウステクニックの一つを紹介します

プログラムのある処理で、マウスが押されてから離されるまでを1サイクルとする場合
必ず、マウスが離されるまでのメッセージを取得する必要があります
例えば、画像ソフトウェアでマウスをドラッグして長方形を描画する場合などです
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static RECT rct;
	static BOOL blMouse = FALSE;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		blMouse = TRUE;
		rct.left = LOWORD(lp);
		rct.top = HIWORD(lp);
		return 0;
	case WM_LBUTTONUP:
		blMouse = FALSE;
		return 0;
	case WM_MOUSEMOVE:
		if(blMouse) {
			rct.right = LOWORD(lp);
			rct.bottom = HIWORD(lp);
			InvalidateRect(hwnd , NULL , TRUE);
		}
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		Rectangle(hdc , rct.left , rct.top , rct.right , rct.bottom);
		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}


このプログラムは、まずクライアント領域でマウスの左ボタンを押します
その状態で移動する(ドラッグ)すると、押した位置を始点とした長方形を描画します
そして、ボタンを離すと長方形の大きさが決定されます

一見、このプログラムには問題がないように思われます
しかし、ドラッグしている状態でクライアントエリアの外でボタンを離す
そのメッセージはウィンドウプロシージャに送られてこないため、処理できません



この状態でウィンドウに再びマウスを戻すと
プログラムはまだマウスのボタンが押された状態だと思いこんでいます

このような問題を避けるための機能が、マウスキャプチャーです
これは、ボタンが離されるまでウィンドウの外にマウスが移動しても
そのウィンドウがマウスを制御することができるという機能です
マウスをキャプチャーするには SetCapture() 関数を使います

HWND SetCapture(HWND hWnd);

hWnd には、マウスをキャプチャーするウィンドウのハンドルを指定します
戻り値は、以前にマウスをキャプチャーしていたウィンドウのハンドルが返ります
そのようなウィンドウがない場合は NULL になります

マウスをキャプチャーした場合、キャプチャーの必要がなくなれば解放する必要があります
マウスのキャプチャーを解放するには ReleaseCapture() を使います

BOOL ReleaseCapture(VOID);

成功すると 0 以外、失敗すると 0 が返ります
ただし、マウスキャプチャーについては Windows が厳しく取り締まっており
あくまでボタンの解放メッセージを受け取るためのものと考えるべきです

キャプチャーと言っても、マウスのボタンが押され他のウィンドウに制御が映ると
その時点で他のウィンドウに制御が映り、マウスメッセージはそのウィンドウに送られます
これは、プログラムがいつまでもキャプチャーを解放せずにいた時
システム全体に影響を与えないための処置です
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static RECT rct;
	static BOOL blMouse = FALSE;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_LBUTTONDOWN:
		blMouse = TRUE;
		rct.left = LOWORD(lp);
		rct.top = HIWORD(lp);
		SetCapture(hwnd);
		return 0;
	case WM_LBUTTONUP:
		blMouse = FALSE;
		ReleaseCapture();
		return 0;
	case WM_MOUSEMOVE:
		if(blMouse) {
			rct.right = LOWORD(lp);
			rct.bottom = HIWORD(lp);
			InvalidateRect(hwnd , NULL , TRUE);
		}
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		Rectangle(hdc , rct.left , rct.top , rct.right , rct.bottom);
		EndPaint(hwnd , &ps);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}
先ほどのプログラムを改良したものです
マウスがウィンドウの外に移動しても、メッセージを受け取ることが確認できるでしょう
この時のマウスの座標もクライアント座標で、クライアントより左上にある場合負数になります


SetCapture()

HWND SetCapture(HWND hWnd);

マウスをキャプチャーします

hWnd - ウィンドウのハンドルを指定します

戻り値 - 以前マウスをキャプチャーしていたウィンドウのハンドル。ない場合は NULL

ReleaseCapture()

BOOL ReleaseCapture(VOID);

マウスのキャプチャーを解放します

戻り値 - 関数が成功すると 0 以外、失敗すると 0



前のページへ戻る次のページへ