作者:樊一鵬
前回同大家簡單介紹了如何用 DirectInput
來進行鍵盤編程,本回所要講述的就該是關于如何使用 DirectInput
來對另一個非常重要的輸入設備----鼠標的編程問題。
鼠標的編程同鍵盤編程的過程是非常相似的,有了上次的基礎,你很快就能看出兩者在形式上其實完全是大同小異的。
DIRECTINPUT 的初始化
同前面講鍵盤編程的過程一樣,我們還是先從
DIRECTINPUT 的初始化開始吧:
#include <dinput.h>
#define DINPUT_BUFFERSIZE 16
LPDIRECTINPUT lpDirectInput; // DirectInput object
LPDIRECTINPUTDEVICE lpMouse; // DirectInput device
BOOL InitDInput(HWND hWnd)
{
HRESULT hr;
// 創建一個 DIRECTINPUT 對象
hr = DirectInputCreate(hInstanceCopy, DIRECTINPUT_VERSION, &lpDirectInput, NULL);
if FAILED(hr)
{
// 失敗
return FALSE;
}
// 創建一個 DIRECTINPUTDEVICE 界面
hr = lpDirectInput->CreateDevice(GUID_SysMouse, &lpMouse, NULL);
if FAILED(hr)
{
// 失敗
return FALSE;
}
// 設定查詢鼠標狀態的返回數據格式
hr = lpMouse->SetDataFormat(&c_dfDIMouse);
if FAILED(hr)
{
// 失敗
return FALSE;
}
// 設定協作模式
hr = lpMouse->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if FAILED(hr)
{
// 失敗
return FALSE;
}
// 設定緩沖區大小
// 如果不設定,緩沖區大小默認值為 0,程序就只能按立即模式工作
// 如果要用緩沖模式工作,必須使緩沖區大小超過 0
DIPROPDWORD property;
property.diph.dwSize = sizeof(DIPROPDWORD);
property.diph.dwHeaderSize = sizeof(DIPROPHEADER);
property.diph.dwObj = 0;
property.diph.dwHow = DIPH_DEVICE;
property.dwData = DINPUT_BUFFERSIZE;
hr = lpMouse->SetProperty(DIPROP_BUFFERSIZE, &property.diph);
if FAILED(hr)
{
// 失敗
return FALSE;
}
hr = lpMouse->Acquire();
if FAILED(hr)
{
// 失敗
return FALSE;
}
return TRUE;
}
除了少數幾處改動以外,這段代碼同前面講的鍵盤編程的代碼基本上完全一樣。
注意調用 CreateDevice 來建立一個
DIRECTINPUTDEVICE 界面時,我們用的參數是
GUID_SysMouse 而不是 GUID_SysKeyboard,我們以此來指明了建立的是鼠標對象。相應的,在用
SetDataFormat
來設定返回數據的格式時,我們用的參數是
&c_dfDIMouse 而不是 &c_dfDIKeyboard。
還有要特別注意的是,前面講到的鍵盤,在設置協作方式時,是只能按非獨占方式工作的,而鼠標即可以按非獨占方式工作,也可以按獨占方式工作。
DIRECTINPUT 的數據查詢
在作 DIRECTINPUT
的鼠標數據查詢時,我一般都是使用的緩沖模式而不是立即模式。原因很簡單,因為鼠標移動事件的頻率很高,按立即模式去處理就很難保證不丟失數據。至于
DIRECTX SDK
里的例程使用立即模式讀取數據則是因為它們用了一個多媒體計時器來保證以每秒三十次的頻率處理接受鼠標數據。
明白這一點后,下面我們就來看看相應的代碼。由于
DIRECTX SDK
里的例程有立即模式的代碼,我就偷點懶,下面只給出了一段緩沖模式下的代碼:
HRESULT UpdateInputState(void)
{
DWORD i;
if(lpMouse != NULL)
{
DIDEVICEOBJECTDATA didod; // Receives buffered data
DWORD dwElements;
HRESULT hr;
while(TRUE)
{
dwElements = 1; // 每次從緩沖區中讀一個數據
hr = lpMouse->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), &didod, &dwElements, 0);
if FAILED(hr)
{
// 發生了一個錯誤
if(hr == DIERR_INPUTLOST)
{
hr = lpMouse->Acquire(); // 試圖重新取回設備
if FAILED(hr)
{
return S_FALSE; // 失敗
}
}
}
else
if(elements == 1)
{
switch(didod.dwOfs)
{
case DIMOFS_X: // X 軸偏移量
// didod.dwData 里是具體偏移相對值,單位為像素
break;
case DIMOFS_Y: // Y 軸偏移量
// didod.dwData 里是具體偏移相對值,單位為像素
break;
case DIMOFS_BUTTON0: // 0 號鍵(左鍵)狀態
// didod.dwData 里是具體狀態值
// 低字節最高位為 1 則表示按下
// 低字節最高位為 0 表示未按下
break;
case DIMOFS_BUTTON1: // 1 號鍵(右鍵)狀態
// 同上
break;
case DIMOFS_BUTTON2: // 2 號鍵(中鍵)狀態
// 同上
break;
case DIMOFS_BUTTON3: // 3 號鍵狀態
// 同上
break;
}
}
else if (elements == 0) break; // 緩沖區讀空
}
}
return S_OK;
}
這段代碼注釋得非常詳細,相信你很快就能看懂。
DIRECTINPUT 的結束處理
還記得當程序結束時必須要進行的釋放處理吧,其代碼如下:
void ReleaseDInput(void)
{
if (lpDirectInput)
{
if(lpMouse)
{
// Always unacquire the device before calling Release().
lpMouse->Unacquire();
lpMouse->Release();
lpMouse = NULL;
}
lpDirectInput->Release();
lpDirectInput = NULL;
}
}