在我的上一篇博客中(/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_OBJECTpublic: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() 函数开始监听就可以了。
当然在监听之前,还要连接好需要的信号和槽。