マウスの状態取得
「マウスの状態を取得する」のは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を使ったこのプログラムの場合は、マウスの状態をそのまま取得するだけのようですが、「そのまま取得している」ということは、扱い方次第で
- どちらのボタンを先に押して
- どのくらい両方のボタンが押されて
- どちらのボタンを先に離したか
までを知ることができます。

