その他

マウスの状態取得(DirectInput)

マウスの状態取得

「マウスの状態を取得する」のはWindowsプログラムでは必須となりますが、通常は「マウスでクリックされた」ところから扱うことがほとんどです。
今回もDirectInputを使った実装方法ついて書きます。

 

DirectInputでマウスから取得できる情報

DirectInputを使ったマウスデバイスから取得できる情報には

  • マウスポインタの移動量X
  • マウスポインタの移動量Y
  • マウスポインタの移動量Z(2016時点でZはまだ出番はなさそうですね)
  • マウス左ボタンの押下状態
  • マウス右ボタンの押下状態
  • マウス中ボタンの押下状態(最近のホイールマウスのホイールを押してもここは反応しません。3ボタンマウスというマウスのときに使用されるものです)

があります。

 

実装してみる

実装したコード

//-----------------------------------------------------------------------------
//
// This program created from 'Win32 project'.
//
//-----------------------------------------------------------------------------
#include <windows.h>
#include <stdio.h>

// ここから、DirectInputで必要なコード -->
#define DIRECTINPUT_VERSION		0x0800		// DirectInputのバージョン情報
#include <dinput.h>

#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
// --> ここまで、DirectInputで必要なコード

//-----------------------------------------------------------------------------
// 定数
//-----------------------------------------------------------------------------
#define APP_NAME			"DInputMouseTest"			// このプログラムの名前
#define APP_TITLE			"DirectInput Mouse test"	// このプログラムのタイトル
#define SCREEN_WIDTH		(640)
#define SCREEN_HEIGHT		(480)


//-----------------------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------------------
// ここから、DirectInputで必要なコード -->
LPDIRECTINPUT8       g_pDInput = NULL;	// DirectInputオブジェクト

// マウス用
LPDIRECTINPUTDEVICE8 g_pDIMouse = NULL;	// マウスデバイス
DIMOUSESTATE g_zdiMouseState;			// マウス状態
// --> ここまで、DirectInputで必要なコード

BOOL g_bAppActive = FALSE;			// アプリケーションアクティブフラグ

// ここから、DirectInputで必要なコード -->
//-----------------------------------------------------------------------------
//
// DirectInput用関数
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// DirectInputの初期化処理
//-----------------------------------------------------------------------------
bool InitDInput(HINSTANCE hInstApp, HWND hWnd)
{
	HRESULT ret = DirectInput8Create(hInstApp, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&g_pDInput, NULL);
	if (FAILED(ret)) {
		return false;	// 作成に失敗
	}
	return true;
}
//-----------------------------------------------------------------------------
// DirectInputの終了処理
//-----------------------------------------------------------------------------
bool ReleaseDInput(void)
{
	// DirectInputのデバイスを開放
	if (g_pDInput) {
		g_pDInput->Release();
		g_pDInput = NULL;
	}

	return true;
}

//-----------------------------------------------------------------------------
//
// DirectInput(Mouse)用関数
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// DirectInputのマウスデバイス用の初期化処理
//-----------------------------------------------------------------------------
bool InitDInputMouse(HWND hWnd)
{
	HRESULT ret = S_FALSE;
	if (g_pDInput == NULL) {
		return false;
	}

	// マウス用にデバイスオブジェクトを作成
	ret = g_pDInput->CreateDevice(GUID_SysMouse, &g_pDIMouse, NULL);
	if (FAILED(ret)) {
		// デバイスの作成に失敗
		return false;
	}

	// データフォーマットを設定
	ret = g_pDIMouse->SetDataFormat(&c_dfDIMouse);	// マウス用のデータ・フォーマットを設定
	if (FAILED(ret)) {
		// データフォーマットに失敗
		return false;
	}

	// モードを設定(フォアグラウンド&非排他モード)
	ret = g_pDIMouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
	if (FAILED(ret)) {
		// モードの設定に失敗
		return false;
	}

	// デバイスの設定
	DIPROPDWORD diprop;
	diprop.diph.dwSize = sizeof(diprop);
	diprop.diph.dwHeaderSize = sizeof(diprop.diph);
	diprop.diph.dwObj = 0;
	diprop.diph.dwHow = DIPH_DEVICE;
	diprop.dwData = DIPROPAXISMODE_REL;	// 相対値モードで設定(絶対値はDIPROPAXISMODE_ABS)

	ret = g_pDIMouse->SetProperty(DIPROP_AXISMODE, &diprop.diph);
	if (FAILED(ret)) {
		// デバイスの設定に失敗
		return false;
	}

	// 入力制御開始
	g_pDIMouse->Acquire();

	return true;
}
//-----------------------------------------------------------------------------
// DirectInputのマウスデバイス用の解放処理
//-----------------------------------------------------------------------------
bool ReleaseDInputMouse()
{
	// DirectInputのデバイスを開放
	if (g_pDIMouse) {
		g_pDIMouse->Release();
		g_pDIMouse = NULL;
	}

	return true;

}
// --> ここまで、DirectInputで必要なコード

//-----------------------------------------------------------------------------
// DirectInputのマウスデバイス状態取得処理
//-----------------------------------------------------------------------------
void GetMouseState(HWND hWnd)
{
	if (g_pDIMouse == NULL) {
		// オブジェクト生成前に呼ばれたときはここで生成させる
		InitDInputMouse(hWnd);
	}

	// 読取前の値を保持します
	DIMOUSESTATE g_zdiMouseState_bak;	// マウス情報(変化検知用)
	memcpy(&g_zdiMouseState_bak, &g_zdiMouseState, sizeof(g_zdiMouseState_bak));

// ここから、DirectInputで必要なコード -->
	// マウスの状態を取得します
	HRESULT	hr = g_pDIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &g_zdiMouseState);
	if (hr == DIERR_INPUTLOST) {
		g_pDIMouse->Acquire();
		hr = g_pDIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &g_zdiMouseState);
	}
// --> ここまで、DirectInputで必要なコード

	if (memcmp(&g_zdiMouseState_bak, &g_zdiMouseState, sizeof(g_zdiMouseState_bak)) != 0) {
// 確認用の処理、ここから -->
		// 値が変わったら表示します
		char buf[128];
		wsprintf(buf, "(%5d, %5d, %5d) %s %s %s\n",
			g_zdiMouseState.lX, g_zdiMouseState.lY, g_zdiMouseState.lZ,
			(g_zdiMouseState.rgbButtons[0] & 0x80) ? "Left" : "--",
			(g_zdiMouseState.rgbButtons[1] & 0x80) ? "Right" : "--",
			(g_zdiMouseState.rgbButtons[2] & 0x80) ? "Center" : "--");
		OutputDebugString(buf);
// --> ここまで、確認用の処理
	}
}


//-----------------------------------------------------------------------------
//
// Windowsアプリケーション関数
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// メッセージハンドラ
//-----------------------------------------------------------------------------
LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_ACTIVATE:	// アクティブ時:1 非アクティブ時:0
		if (wParam == 0) {
			// 非アクティブになった場合
			ReleaseDInputMouse();
		}
		return 0L;
	case WM_DESTROY:	// アプリケーション終了時の処理
// ここから、DirectInputで必要なコード -->
		ReleaseDInputMouse();	// DirectInput(Mouse)オブジェクトの開放
		ReleaseDInput();		// DirectInputオブジェクトの開放
// --> ここまで、DirectInputで必要なコード
		// プログラムを終了します
		PostQuitMessage(0);
		return 0L;
	case WM_SETCURSOR:	// カーソルの設定
		SetCursor(NULL);
		return TRUE;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

//-----------------------------------------------------------------------------
// ウィンドウ初期化(生成)処理
//-----------------------------------------------------------------------------
HWND InitializeWindow(HINSTANCE hThisInst, int nWinMode)
{
	WNDCLASS wc;
	HWND     hWnd;		// Window Handle

	// ウィンドウクラスを定義する
	wc.hInstance = hThisInst;					// このインスタンスへのハンドル
	wc.lpszClassName = APP_NAME;				// ウィンドウクラス名
	wc.lpfnWndProc = WinProc;					// ウィンドウ関数
	wc.style = CS_HREDRAW | CS_VREDRAW;			// ウィンドウスタイル
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);	// アイコン
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);	// カーソルスタイル
	wc.lpszMenuName = APP_NAME;					// メニュー(なし)
	wc.cbClsExtra = 0;							// エキストラ(なし)
	wc.cbWndExtra = 0;							// 必要な情報(なし)
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);	// ウィンドウの背景(黒)

	// ウィンドウクラスを登録する
	if (!RegisterClass(&wc))
		return NULL;

	// ウィンドウクラスの登録ができたので、ウィンドウを生成する
	hWnd = CreateWindowEx(WS_EX_TOPMOST,
		APP_NAME,				// ウィンドウクラスの名前
		APP_TITLE,				// ウィンドウタイトル
		WS_OVERLAPPEDWINDOW,	// ウィンドウスタイル(ノーマル)
		0,						// ウィンドウ左角X座標
		0,						// ウィンドウ左角Y座標
		SCREEN_WIDTH,			// ウィンドウの幅
		SCREEN_HEIGHT,			// ウィンドウの高さ
		NULL,					// 親ウィンドウ(なし)
		NULL,					// メニュー(なし)
		hThisInst,				// このプログラムのインスタンスのハンドル
		NULL					// 追加引数(なし)
		);

	if (!hWnd) {
		return NULL;
	}

	// ウィンドウを表示する
	ShowWindow(hWnd, nWinMode);
	UpdateWindow(hWnd);
	SetFocus(hWnd);

	return hWnd;
}

//-----------------------------------------------------------------------------
// プログラムエントリーポイント(WinMain)
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR lpszArgs, int nWinMode)
{
	MSG  msg;
	HWND hWnd;

	/* 表示するウィンドウの定義、登録、表示 */
	if (!(hWnd = InitializeWindow(hThisInst, nWinMode))) {
		return FALSE;
	}

// ここから、DirectInputで必要なコード -->
	InitDInput(hThisInst, hWnd);
	InitDInputMouse(hWnd);
// --> ここまで、DirectInputで必要なコード

	while (TRUE)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if (!GetMessage(&msg, NULL, 0, 0)) {
				break;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		// Check and get mouse state
// ここから、DirectInputで必要なコード -->
		GetMouseState(hWnd);
// --> ここまで、DirectInputで必要なコード
	}

	return msg.wParam;
}

 

 

コードの説明

9行目から15行目

// ここから、DirectInputで必要なコード -->
#define DIRECTINPUT_VERSION		0x0800		// DirectInputのバージョン情報
#include <dinput.h>

#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
// --> ここまで、DirectInputで必要なコード

ヘッダファイルのインクルード文と使用するライブラリファイルの指定です。
(キーボードの時にも書いたところですが、”DIRECTINPUT_VERSION”の#define文がある行は、#include <dinput.h>より”前”に書く必要があります)

 

29行目から35行目

// ここから、DirectInputで必要なコード -->
LPDIRECTINPUT8       g_pDInput = NULL;	// DirectInputオブジェクト

// マウス用
LPDIRECTINPUTDEVICE8 g_pDIMouse = NULL;	// マウスデバイス
DIMOUSESTATE g_zdiMouseState;			// マウス状態
// --> ここまで、DirectInputで必要なコード

ここは、使用するDirectInputのオブジェクト(g_pDInput)と、DirectInputのマウスデバイスオブジェクト(g_pDIMouse)の宣言を行っています。
また、マウスデバイスから取得したマウス状態を保持するグローバル変数(g_zdiMouseState)も宣言しています。

 

39行目から139行目

// ここから、DirectInputで必要なコード -->
//-----------------------------------------------------------------------------
//
// DirectInput用関数
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// DirectInputの初期化処理
//-----------------------------------------------------------------------------
bool InitDInput(HINSTANCE hInstApp, HWND hWnd)
{
	HRESULT ret = DirectInput8Create(hInstApp, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&g_pDInput, NULL);
	if (FAILED(ret)) {
		return false;	// 作成に失敗
	}
	return true;
}
//-----------------------------------------------------------------------------
// DirectInputの終了処理
//-----------------------------------------------------------------------------
bool ReleaseDInput(void)
{
	// DirectInputのデバイスを開放
	if (g_pDInput) {
		g_pDInput->Release();
		g_pDInput = NULL;
	}

	return true;
}

//-----------------------------------------------------------------------------
//
// DirectInput(Mouse)用関数
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// DirectInputのマウスデバイス用の初期化処理
//-----------------------------------------------------------------------------
bool InitDInputMouse(HWND hWnd)
{
	HRESULT ret = S_FALSE;
	if (g_pDInput == NULL) {
		return false;
	}

	// マウス用にデバイスオブジェクトを作成
	ret = g_pDInput->CreateDevice(GUID_SysMouse, &g_pDIMouse, NULL);
	if (FAILED(ret)) {
		// デバイスの作成に失敗
		return false;
	}

	// データフォーマットを設定
	ret = g_pDIMouse->SetDataFormat(&c_dfDIMouse);	// マウス用のデータ・フォーマットを設定
	if (FAILED(ret)) {
		// データフォーマットに失敗
		return false;
	}

	// モードを設定(フォアグラウンド&非排他モード)
	ret = g_pDIMouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
	if (FAILED(ret)) {
		// モードの設定に失敗
		return false;
	}

	// デバイスの設定
	DIPROPDWORD diprop;
	diprop.diph.dwSize = sizeof(diprop);
	diprop.diph.dwHeaderSize = sizeof(diprop.diph);
	diprop.diph.dwObj = 0;
	diprop.diph.dwHow = DIPH_DEVICE;
	diprop.dwData = DIPROPAXISMODE_REL;	// 相対値モードで設定(絶対値はDIPROPAXISMODE_ABS)

	ret = g_pDIMouse->SetProperty(DIPROP_AXISMODE, &diprop.diph);
	if (FAILED(ret)) {
		// デバイスの設定に失敗
		return false;
	}

	// 入力制御開始
	g_pDIMouse->Acquire();

	return true;
}
//-----------------------------------------------------------------------------
// DirectInputのマウスデバイス用の解放処理
//-----------------------------------------------------------------------------
bool ReleaseDInputMouse()
{
	// DirectInputのデバイスを開放
	if (g_pDIMouse) {
		g_pDIMouse->Release();
		g_pDIMouse = NULL;
	}

	return true;

}
// --> ここまで、DirectInputで必要なコード

ここはちょっと長いです。

関数が4つ実装されています。

  • bool InitDInput(HINSTANCE hInstApp, HWND hWnd)
    → DirectInputの初期化処理呼び出し用の関数
  • bool ReleaseDInput(void)
    → DirectInputの終了処理呼び出し用の関数
  • bool InitDInputMouse(HWND hWnd)
    → DirectInputのマウスデバイス用の初期化処理呼び出し用関数
  • bool ReleaseDInputMouse()
    → DirectInputのマウスデバイス用の解放処理呼び出し用関数

これらの関数はそれぞれ、下記の追加部分から呼び出されます。

155行目から162行目

// ここから、DirectInputで必要なコード -->
	// マウスの状態を取得します
	HRESULT	hr = g_pDIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &g_zdiMouseState);
	if (hr == DIERR_INPUTLOST) {
		g_pDIMouse->Acquire();
		hr = g_pDIMouse->GetDeviceState(sizeof(DIMOUSESTATE), &g_zdiMouseState);
	}
// --> ここまで、DirectInputで必要なコード

ここが、DirectInputでマウス状態を取得する「実体」になります。

DirectInputのGetDeviceState()を呼び出すことで、マウスデバイスが現在持っている状態を取得します。

 

198行目から201行目

// ここから、DirectInputで必要なコード -->
		ReleaseDInputMouse();	// DirectInput(Mouse)オブジェクトの開放
		ReleaseDInput();		// DirectInputオブジェクトの開放
// --> ここまで、DirectInputで必要なコード

ここは、ウィンドウメッセージの’WM_DESTROY’から呼び出されたときの処理です。
プログラム自体が終了する箇所なので、DirectInput関係のオブジェクトも破棄して、プログラムが終了する準備をします。

 

277行目から280行目

// ここから、DirectInputで必要なコード -->
	InitDInput(hThisInst, hWnd);
	InitDInputMouse(hWnd);
// --> ここまで、DirectInputで必要なコード

ここは、WinMain関数(Win32プログラムのエントリポイント)が呼び出され、メッセージループに入るまでの処理です。

「プログラムの初期化処理」で、ここで

  • DirectInputオブジェクトの生成と初期化
    (InitDInput()関数の呼び出し)
  • DirectInputマウスデバイスオブジェクトの生成と初期化
    (InitDInputMouse()関数の呼び出し)

を行っておきます。

 

293行目から295行目

// ここから、DirectInputで必要なコード -->
		GetMouseState(hWnd);
// --> ここまで、DirectInputで必要なコード

ここはメッセージループ本体(ほんとにループしている個所です)にGetMouseState関数を呼び出すことで、155行目から162行目の処理が実行されます。

DirectInputのGetDeviceState()が呼び出されるので、ここでマウス状態の取得処理が行っていることになります。

 

まとめ

このプログラムを動かしてみると、なんとなく「マウスの情報をデバッグ出力(Visual Studioの[出力]タブ)に出力しているだけ」に見えます。

ただ、このプログラムを実行させて、マウスの左ボタンと右ボタンを同時に押してみてください。

通常のWindowsプログラムでは、マウスの左ボタンと右ボタンを同時に押すと、たいてい「左ボタンが押された」「右ボタンが押された」は判りますが、「左ボタンと右ボタンが両方押されている」は判りません。

DirectInputを使ったこのプログラムの場合は、マウスの状態をそのまま取得するだけのようですが、「そのまま取得している」ということは、扱い方次第で

  • どちらのボタンを先に押して
  • どのくらい両方のボタンが押されて
  • どちらのボタンを先に離したか

までを知ることができます。

 

 

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