在我的上一篇博客中(http://blog.csdn.net/liyuanbhu/article/details/51714045),介绍了通过 multimedia joystick API 来访问游戏手柄的基本方法。
最后说到了利用 joySetCapture 函数监听手柄事件的方式并不是非常的好用。建议大家字节写个监听线程,有针对性的监听需要的事件。这里,我把我以前写的一份代码放上来。代码是基于 Qt 的,监听了按键、摇杆和视觉头盔的状态,在状态发生改变时发出Qt 的信号。
按键对应两个信号:
void Joy_ButtonPressed(int i);
void Joy_ButtonReleased(int i);
摇杆分为两种模式:SWITCH_MODE 和 COORDINATE_MODE
SWITCH_MODE 模式下,输出四个方向的开关量。对应的信号是:
void Joy_MoveForward();
void Joy_MoveBackward();
void Joy_MoveForwardStop();
void Joy_MoveBackwardStop();
void Joy_MoveLeft();
void Joy_MoveRight();
void Joy_MoveLeftStop();
void Joy_MoveRightStop();
COORDINATE_MODE 模式直接输出坐标值(只有在摇杆位置发生变化时才会有输出)。
void Joy_Position(int x, int y);
视觉头盔模拟了摇杆的开关量模式。对应的信号同样是:
void Joy_MoveForward();
void Joy_MoveBackward();
void Joy_MoveForwardStop();
void Joy_MoveBackwardStop();
void Joy_MoveLeft();
void Joy_MoveRight();
void Joy_MoveLeftStop();
void Joy_MoveRightStop();
具体的代码不用过多介绍,主要就是几个状态机。下面贴出代码来。
首先是头文件:
#ifndef JOYSTICK_H
#define JOYSTICK_H
#include <QObject>
#include <QThread>
#include <Windows.h>
#include <Mmsystem.h>
class JoyStick;
/**
* @brief The JoyStickThread class
* @details 内部类,定时轮询 JoyStick 的状态。
*/
class JoyStickThread : public QThread
{
public:
explicit JoyStickThread();
void setJoyStick(JoyStick *joystick);
void run();
void stop();
private:
void Coordinate_StateMachine(int xPos, int yPos);
void AxisX_StateMachine(int xPos);
void AxisY_StateMachine(int yPos);
void Button_StateMachine(int button);
void POV_StateMachine_Axis(int pov);
void POV_StateMachine(int pov);
int old_xPos;
int old_yPos;
int old_pov;
bool pov_forward;
bool pov_backward;
bool pov_left;
bool pov_right;
bool pov_center;
int m_button[16];
JoyStick *m_joystick;
bool m_run;
};
class JoyStick : public QObject
{
Q_OBJECT
public:
enum MODE{SWITCH_MODE, COORDINATE_MODE};
/**
* @brief JoyStick
* @param mode SWITCH_MODE 模式时,摇杆输出开关量。COORDINATE_MODE 模式时,输出摇杆的位置。
* @param parent
*/
explicit JoyStick(enum MODE mode = SWITCH_MODE, QObject *parent = 0);
enum MODE mode() const {return m_mode;}
~JoyStick();
/**
* @brief listen
* @return 启动监听线程,之后就可以接收手柄的各种消息了。
*/
bool listen();
/**
* @brief stop 停止监听线程,不需要接收手柄消息时可以调用这个函数。
*/
void stop();
signals:
/// JoyStick 的摇杆的各种信号。
/// 高级些的手柄是可以返回摇杆的坐标值的。
/// 这里模仿的是工业摇杆,输出的都是开关量。
/// 前后左右四组开关量。
void Joy_MoveForward();
void Joy_MoveBackward();
void Joy_MoveForwardStop();
void Joy_MoveBackwardStop();
void Joy_MoveLeft();
void Joy_MoveRight();
void Joy_MoveLeftStop();
void Joy_MoveRightStop();
/// JoyStick 的摇杆位置。
void Joy_Position(int x, int y);
/**
* @brief Joy_ButtonPressed 当手柄上某个按键被按下时发出这个信号
* @param i 用来标志是哪个按键被按下了。
*/
void Joy_ButtonPressed(int i);
/**
* @brief Joy_ButtonReleased 当手柄上某个按键被释放时发出这个信号
* @param i 用来标志是哪个按键被释放了。
*/
void Joy_ButtonReleased(int i);
/// 以下是视觉头盔的各种信号
void Joy_POVForward();
void Joy_POVBackward();
void Joy_POVLeft();
void Joy_POVRight();
void Joy_POVForwardLeft();
void Joy_POVForwardRight();
void Joy_POVBackwardLeft();
void Joy_POVBackwardRight();
/**
* @brief Joy_POVCentered 当视觉头盔从前8种状态恢复到默认状态时发出这个信号。
*/
void Joy_POVCentered();
private:
enum MODE m_mode;
bool m_valid;
JoyStickThread m_thread;
};
#endif // JOYSTICK_H
下面是cpp 文件:
#include "joystick.h"
#include <Windows.h>
#include <QDebug>
JoyStick::JoyStick(enum MODE mode, QObject *parent) : QObject(parent)
{
m_mode = mode;
m_valid = false;
m_thread.setJoyStick(this);
JOYINFO joyinfo;
if( joyGetNumDevs() > 0 && joyGetPos(JOYSTICKID1, &joyinfo) != JOYERR_UNPLUGGED )
{
qDebug() << "JoyStick Init success!";
m_valid = true;
}
}
JoyStick::~JoyStick()
{
m_thread.stop();
m_thread.wait();
}
bool JoyStick::listen()
{
m_thread.start();
return true;
}
void JoyStick::stop()
{
if(m_thread.isRunning())
{
m_thread.stop();
m_thread.wait();
}
}
JoyStickThread::JoyStickThread()
{
m_run = 1;
old_xPos = 32767;
old_yPos = 32767;
for(int i = 0; i < 16; i++)
{
m_button[i] = 0;
}
old_pov = JOY_POVCENTERED;
pov_forward = false;
pov_backward = false;
pov_left = false;
pov_right = false;
pov_center = true;
}
void JoyStickThread::stop()
{
m_run = 0;
}
void JoyStickThread::setJoyStick(JoyStick *joystick)
{
m_joystick = joystick;
}
void JoyStickThread::run()
{
//JOYINFO joyinfo;
JOYINFOEX joyinfoex;
joyinfoex.dwSize = sizeof(JOYINFOEX);
joyinfoex.dwFlags = JOY_RETURNALL;
while(m_run)
{
// if(joyGetPos(JOYSTICKID1, &joyinfo) == JOYERR_NOERROR)
// {
// //qDebug() << joyinfo.wXpos;
// AxisX_StateMachine(joyinfo.wXpos);
// AxisY_StateMachine(joyinfo.wYpos);
// Button_StateMachine(joyinfo.wButtons);
// }
if(joyGetPosEx(JOYSTICKID1, &joyinfoex) == JOYERR_NOERROR)
{
if(m_joystick->mode() == JoyStick::SWITCH_MODE)
{
AxisX_StateMachine(joyinfoex.dwXpos);
AxisY_StateMachine(joyinfoex.dwYpos);
}
else
{
Coordinate_StateMachine(joyinfoex.dwXpos, joyinfoex.dwYpos);
}
Button_StateMachine(joyinfoex.dwButtons);
POV_StateMachine_Axis(joyinfoex.dwPOV);
}
msleep(50);
}
}
void JoyStickThread::POV_StateMachine(int pov)
{
if(pov != old_pov)
{
//qDebug() << pov;
if( pov == JOY_POVFORWARD )
{
emit m_joystick->Joy_POVForward();
}
if( pov == JOY_POVFORWARD + 4500 )
{
emit m_joystick->Joy_POVForwardRight();
}
if( pov == JOY_POVRIGHT )
{
emit m_joystick->Joy_POVRight();
}
if( pov == JOY_POVRIGHT + 4500 )
{
emit m_joystick->Joy_POVBackwardRight();
}
if( pov == JOY_POVBACKWARD )
{
emit m_joystick->Joy_POVBackward();
}
if( pov == JOY_POVBACKWARD + 4500 )
{
emit m_joystick->Joy_POVBackwardLeft();
}
if( pov == JOY_POVLEFT )
{
emit m_joystick->Joy_POVLeft();
}
if( pov == JOY_POVLEFT + 4500 )
{
emit m_joystick->Joy_POVForwardLeft();
}
if( pov == JOY_POVCENTERED )
{
emit m_joystick->Joy_POVCentered();
}
old_pov = pov;
}
}
void JoyStickThread::POV_StateMachine_Axis(int pov)
{
if(pov != old_pov)
{
//qDebug() << pov;
if( pov == JOY_POVFORWARD )
{
if(pov_forward == false)
{
pov_forward = true;
emit m_joystick->Joy_MoveForward();
//qDebug() << "JOY_POVFORWARD";
}
if(pov_left == true)
{
emit m_joystick->Joy_MoveLeftStop();
//qDebug() << "JOY_POVLEFT RETURN";
}
if(pov_right == true)
{
emit m_joystick->Joy_MoveRightStop();
//qDebug() << "JOY_POVRIGHT RETURN";
}
pov_left = false;
pov_right = false;
}
if( pov == JOY_POVFORWARD + 4500 )
{
if(pov_forward == false)
{
pov_forward = true;
emit m_joystick->Joy_MoveForward();
//qDebug() << "JOY_POVFORWARD";
}
if(pov_right == false)
{
pov_right = true;
emit m_joystick->Joy_MoveRight();
//qDebug() << "JOY_POVRIGHT";
}
}
if( pov == JOY_POVRIGHT )
{
if(pov_right == false)
{
pov_right = true;
emit m_joystick->Joy_MoveRight();
//qDebug() << "JOY_POVRIGHT";
}
if(pov_forward == true)
{
emit m_joystick->Joy_MoveForwardStop();
//qDebug() << "JOY_POVFORWARD RETURN";
}
if(pov_backward == true)
{
emit m_joystick->Joy_MoveBackwardStop();
//qDebug() << "JOY_POVBACKWARD RETURN";
}
pov_forward = false;
pov_backward = false;
}
if( pov == JOY_POVRIGHT + 4500 )
{
if(pov_right == false)
{
pov_right = true;
emit m_joystick->Joy_MoveRight();
// qDebug() << "JOY_POVRIGHT";
}
if(pov_backward == false)
{
pov_backward = true;
emit m_joystick->Joy_MoveBackward();
// qDebug() << "JOY_POVBACKWARD";
}
}
if( pov == JOY_POVBACKWARD )
{
if(pov_backward == false)
{
pov_backward = true;
emit m_joystick->Joy_MoveBackward();
//qDebug() << "JOY_POVBACKWARD";
}
if(pov_left == true)
{
emit m_joystick->Joy_MoveLeftStop();
//qDebug() << "JOY_POVLEFT RETURN";
}
if(pov_right == true)
{
emit m_joystick->Joy_MoveRightStop();
//qDebug() << "JOY_POVRIGHT RETURN";
}
pov_left = false;
pov_right = false;
}
if( pov == JOY_POVBACKWARD + 4500 )
{
if(pov_backward == false)
{
pov_backward = true;
emit m_joystick->Joy_MoveBackward();
//qDebug() << "JOY_POVBACKWARD";
}
if(pov_left == false)
{
pov_left = true;
emit m_joystick->Joy_MoveLeft();
// qDebug() << "JOY_POVLEFT";
}
}
if( pov == JOY_POVLEFT )
{
if(pov_left == false)
{
pov_left = true;
emit m_joystick->Joy_MoveLeft();
//qDebug() << "JOY_POVLEFT";
}
if(pov_forward == true)
{
emit m_joystick->Joy_MoveForwardStop();
//qDebug() << "JOY_POVFORWARD RETURN";
}
if(pov_backward == true)
{
emit m_joystick->Joy_MoveBackwardStop();
//qDebug() << "JOY_POVBACKWARD RETURN";
}
pov_backward = false;
pov_forward = false;
}
if( pov == JOY_POVLEFT + 4500 )
{
if(pov_left == false)
{
pov_left = true;
emit m_joystick->Joy_MoveLeft();
//qDebug() << "JOY_POVLEFT";
}
if(pov_forward == false)
{
pov_forward = true;
emit m_joystick->Joy_MoveForward();
//qDebug() << "JOY_POVFORWARD";
}
}
if( pov == JOY_POVCENTERED )
{
if(pov_forward == true)
{
emit m_joystick->Joy_MoveForwardStop();
//qDebug() << "JOY_POVFORWARD RETURN";
}
if(pov_backward == true)
{
emit m_joystick->Joy_MoveBackwardStop();
//qDebug() << "JOY_POVBACKWARD RETURN";
}
if(pov_left == true)
{
emit m_joystick->Joy_MoveLeftStop();
//qDebug() << "JOY_POVLEFT RETURN";
}
if(pov_right == true)
{
emit m_joystick->Joy_MoveRightStop();
//qDebug() << "JOY_POVRIGHT RETURN";
}
pov_forward = false;
pov_right = false;
pov_left = false;
pov_backward = false;
//qDebug() << "JOY_POVCENTERED";
}
old_pov = pov;
}
}
void JoyStickThread::Button_StateMachine(int button)
{
//if( button )
//qDebug() << QString::number(button, 16);
const int mask[16] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768};
for(int i = 0; i < 12; i++)
{
int n = button & mask[i];
if(n ^ m_button[i])
{
m_button[i] = n;
if( n )
{
emit m_joystick->Joy_ButtonPressed(i + 1);
//qDebug() << "ButtonPressed(" << i + 1 << ")";
}
else
{
emit m_joystick->Joy_ButtonReleased(i + 1);
//qDebug() << "ButtonReleased(" << i + 1 << ")";;
}
}
}
}
void JoyStickThread::Coordinate_StateMachine(int xPos, int yPos)
{
if(xPos == old_xPos && yPos == old_yPos)
{
return;
}
old_xPos = xPos;
old_yPos = yPos;
emit m_joystick->Joy_Position(xPos, yPos);
}
void JoyStickThread::AxisX_StateMachine(int xPos)
{
if(xPos == old_xPos) return;
switch(xPos)
{
case 0:
emit m_joystick->Joy_MoveLeft();
break;
case 32767:
if(old_xPos == 0)
{
emit m_joystick->Joy_MoveLeftStop();
}
else
{
emit m_joystick->Joy_MoveRightStop();
}
break;
case 65535:
emit m_joystick->Joy_MoveRight();
break;
}
old_xPos = xPos;
}
void JoyStickThread::AxisY_StateMachine(int yPos)
{
if(yPos == old_yPos) return;
switch(yPos)
{
case 0:
emit m_joystick->Joy_MoveForward();
break;
case 32767:
if(old_xPos == 0)
{
emit m_joystick->Joy_MoveForwardStop();
}
else
{
emit m_joystick->Joy_MoveBackwardStop();
}
break;
case 65535:
emit m_joystick->Joy_MoveBackward();
break;
}
old_yPos = yPos;
}
具体的使用方法很简答,建立对象后,只要调用 listen() 函数开始监听就可以了。
当然在监听之前,还要连接好需要的信号和槽。