天行健 君子当自强而不息

【ZT】DirectInput 鼠标编程入门

作者:樊一鹏

前回同大家简单介绍了如何用 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 == 0break;      // 缓冲区读空
        }
    }

    
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;
    }
}

 

posted on 2007-05-06 19:34 lovedday 阅读(647) 评论(0)  编辑 收藏 引用 所属分类: ■ DirectX 9 Program


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论