マウスの状態取得(DirectInput)
マウスの状態取得
「マウスの状態を取得する」のはWindowsプログラムでは必須となりますが、通常は「マウスでクリックされた」ところから扱うことがほとんどです。
今回もDirectInputを使った実装方法ついて書きます。
DirectInputでマウスから取得できる情報
DirectInputを使ったマウスデバイスから取得できる情報には
- マウスポインタの移動量X
- マウスポインタの移動量Y
- マウスポインタの移動量Z(2016時点でZはまだ出番はなさそうですね)
- マウス左ボタンの押下状態
- マウス右ボタンの押下状態
- マウス中ボタンの押下状態(最近のホイールマウスのホイールを押してもここは反応しません。3ボタンマウスというマウスのときに使用されるものです)
があります。
実装してみる
実装したコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
//----------------------------------------------------------------------------- // // 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行目
1 2 3 4 5 6 7 |
// ここから、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行目
1 2 3 4 5 6 7 |
// ここから、DirectInputで必要なコード --> LPDIRECTINPUT8 g_pDInput = NULL; // DirectInputオブジェクト // マウス用 LPDIRECTINPUTDEVICE8 g_pDIMouse = NULL; // マウスデバイス DIMOUSESTATE g_zdiMouseState; // マウス状態 // --> ここまで、DirectInputで必要なコード |
ここは、使用するDirectInputのオブジェクト(g_pDInput)と、DirectInputのマウスデバイスオブジェクト(g_pDIMouse)の宣言を行っています。
また、マウスデバイスから取得したマウス状態を保持するグローバル変数(g_zdiMouseState)も宣言しています。
39行目から139行目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
// ここから、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行目
1 2 3 4 5 6 7 8 |
// ここから、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行目
1 2 3 4 |
// ここから、DirectInputで必要なコード --> ReleaseDInputMouse(); // DirectInput(Mouse)オブジェクトの開放 ReleaseDInput(); // DirectInputオブジェクトの開放 // --> ここまで、DirectInputで必要なコード |
ここは、ウィンドウメッセージの’WM_DESTROY’から呼び出されたときの処理です。
プログラム自体が終了する箇所なので、DirectInput関係のオブジェクトも破棄して、プログラムが終了する準備をします。
277行目から280行目
1 2 3 4 |
// ここから、DirectInputで必要なコード --> InitDInput(hThisInst, hWnd); InitDInputMouse(hWnd); // --> ここまで、DirectInputで必要なコード |
ここは、WinMain関数(Win32プログラムのエントリポイント)が呼び出され、メッセージループに入るまでの処理です。
「プログラムの初期化処理」で、ここで
- DirectInputオブジェクトの生成と初期化
(InitDInput()関数の呼び出し) - DirectInputマウスデバイスオブジェクトの生成と初期化
(InitDInputMouse()関数の呼び出し)
を行っておきます。
293行目から295行目
1 2 3 |
// ここから、DirectInputで必要なコード --> GetMouseState(hWnd); // --> ここまで、DirectInputで必要なコード |
ここはメッセージループ本体(ほんとにループしている個所です)にGetMouseState関数を呼び出すことで、155行目から162行目の処理が実行されます。
DirectInputのGetDeviceState()が呼び出されるので、ここでマウス状態の取得処理が行っていることになります。
まとめ
このプログラムを動かしてみると、なんとなく「マウスの情報をデバッグ出力(Visual Studioの[出力]タブ)に出力しているだけ」に見えます。
ただ、このプログラムを実行させて、マウスの左ボタンと右ボタンを同時に押してみてください。
通常のWindowsプログラムでは、マウスの左ボタンと右ボタンを同時に押すと、たいてい「左ボタンが押された」「右ボタンが押された」は判りますが、「左ボタンと右ボタンが両方押されている」は判りません。
DirectInputを使ったこのプログラムの場合は、マウスの状態をそのまま取得するだけのようですが、「そのまま取得している」ということは、扱い方次第で
- どちらのボタンを先に押して
- どのくらい両方のボタンが押されて
- どちらのボタンを先に離したか
までを知ることができます。