// -------------------------------------------------------------- joy_ctrl.h
#ifndef _JOYSTICK_CTRL_H_
#define _JOYSTICK_CTRL_H_
#include <windows.h>
/**
* JoyStick(JoyPad) コントロールクラス(PImpl) by ミングスレの名無し
*
* 使用方法:
* WinMain 内で親ウィンドウのハンドル渡して
* start で放置。以後、内部の別スレッドがJoyPadを監視して
* WM_KEYDOWN を飛ばす。十字キーでカーソルキーのコードを飛ばす(VK_XXX)
* しかし現在エラい事になるので改修必須
*/
class JoyStick {
private:
void *pjc;
public:
JoyStick(HWND hWnd);
~JoyStick();
// 監視とメッセージ送信のスレッド開始
void start();
};
#endif
// -------------------------------------------------------------- joy_ctrl.cpp
#include "joy_ctrl.h"
#define DIRECTINPUT_VERSION 0x0800
#ifndef _WIN32_DCOM
#define _WIN32_DCOM
#endif
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dinput8.lib")
#include <dinput.h>
#include <dinputd.h>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
BOOL CALLBACK enumObjCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext );
BOOL CALLBACK enumJSCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext );
// --------------------------------------------------------------------------------
// --
struct DI_ENUM_CONTEXT
{
DIJOYCONFIG* pJoyCfg;
bool bJoyCfgValid;
LPDIRECTINPUT8 *g_pDI;
LPDIRECTINPUTDEVICE8 *g_pJoystick;
};
// --------------------------------------------------------------------------------
// --
class _P_JoyCtrl {
private:
typedef boost::mutex::scoped_lock lock;
boost::mutex mtx;
boost::thread *th;
boost::condition_variable_any cond;
HWND hWnd;
HRESULT hr;
volatile bool bLoop;
LPDIRECTINPUT8 g_pDI;
LPDIRECTINPUTDEVICE8 g_pJoystick;
HRESULT init_dinput();
HRESULT send_dinput_state();
void free_dinput();
void thread_run();
public:
_P_JoyCtrl(HWND);
~_P_JoyCtrl();
void stop();
void start();
};
// --------------------------------------------------------------------------------
// --
_P_JoyCtrl::_P_JoyCtrl(HWND hTarget)
: th(NULL), g_pDI(NULL), g_pJoystick(NULL)
{
hWnd = hTarget;
hr = init_dinput();
}
_P_JoyCtrl::~_P_JoyCtrl()
{
stop();
th->join();
delete th;
free_dinput();
}
void _P_JoyCtrl::start()
{
if(FAILED(hr)) throw std::runtime_error("error: can't start");
if(th != NULL) return;
bLoop = true;
boost::function<void (void)> f = boost::bind(&_P_JoyCtrl::thread_run, this);
th = new boost::thread(f);
}
void _P_JoyCtrl::stop()
{
lock l(mtx);
bLoop = false;
cond.notify_one();
}
void _P_JoyCtrl::thread_run()
{
while(bLoop)
{
lock l(mtx);
if(FAILED(hr = send_dinput_state()))
{
bLoop = false;
break;
}
boost::xtime t;
boost::xtime_get(&t, boost::TIME_UTC);
t.nsec += 1000*300;
if(cond.timed_wait(l, t)) break;
}
}
HRESULT _P_JoyCtrl::init_dinput()
{
IDirectInputJoyConfig8* pJoyConfig = NULL;
if(
FAILED(hr = DirectInput8Create(
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
DIRECTINPUT_VERSION,
IID_IDirectInput8, ( VOID** )&g_pDI, NULL
))
|| FAILED(hr = g_pDI->QueryInterface( IID_IDirectInputJoyConfig8, (void**)&pJoyConfig ))
) return hr;
DIJOYCONFIG prefConf = {0};
DI_ENUM_CONTEXT enCon;
enCon.pJoyCfg = &prefConf;
enCon.bJoyCfgValid = false;
enCon.g_pDI = &g_pDI;
enCon.g_pJoystick = &g_pJoystick;
prefConf.dwSize = sizeof( prefConf );
if(SUCCEEDED(pJoyConfig->GetConfig(0, &prefConf, DIJC_GUIDINSTANCE) )) enCon.bJoyCfgValid = true;
SAFE_RELEASE( pJoyConfig );
if(FAILED(
hr = g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL, enumJSCallback, &enCon, DIEDFL_ATTACHEDONLY)
)) return hr;
hr = E_FAIL;
if(
NULL == g_pJoystick
|| FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick2 ) )
|| FAILED( hr = g_pJoystick->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND ) )
|| FAILED( hr = g_pJoystick->EnumObjects( enumObjCallback, &enCon, DIDFT_ALL ) )
) return hr;
return S_OK;
}
HRESULT _P_JoyCtrl::send_dinput_state()
{
if( NULL == g_pJoystick ) return S_OK;
if(FAILED(hr = g_pJoystick->Poll()))
{
hr = g_pJoystick->Acquire();
while( hr == DIERR_INPUTLOST ) hr = g_pJoystick->Acquire();
return S_OK;
}
DIJOYSTATE2 js;
if(
FAILED(hr = g_pJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &js))
) return hr;
// URLB
// 0001
//const char KEYCHAR[] = {'0','2','4','1', '6','3','0','0', '8','0','7','0', '9','0','0','0'};
const char KEYCHAR[] = {
'0'
, VK_DOWN
, VK_LEFT
, VK_DOWN | VK_LEFT // TODO: ダメ、そうじゃない
, VK_RIGHT
, VK_RIGHT | VK_DOWN
,'0','0'
, VK_UP
,'0'
, VK_UP | VK_LEFT
,'0'
, VK_UP | VK_RIGHT
,'0','0','0'
};
const char BTNCHAR[] = {'A','B','C','D','E', 'F','G','H','I','J'};
BYTE blg = 0;
if(js.lX < LONG(-10)) blg |= 2;
else if(js.lX > LONG(10)) blg |= 4;
if(js.lY < LONG(-10)) blg |= 8;
else if(js.lY > LONG(10)) blg |= 1;
//if(blg) SendMessage(hWnd, WM_CHAR, WPARAM(KEYCHAR[blg]), LPARAM(0));
if(blg) SendMessage(hWnd, WM_KEYDOWN, WPARAM(KEYCHAR[blg]), LPARAM(0));
for(int i=0, ln=sizeof(BTNCHAR)/sizeof(char); i<ln; i++) // ln=128
{
if( js.rgbButtons[i] & 0x80 )
{
SendMessage(hWnd, WM_CHAR, WPARAM(BTNCHAR[i]), LPARAM(0)); // 'A'+i
}
}
return S_OK;
}
void _P_JoyCtrl::free_dinput()
{
if( g_pJoystick ) g_pJoystick->Unacquire();
SAFE_RELEASE( g_pJoystick );
SAFE_RELEASE( g_pDI );
}
// --------------------------------------------------------------------------------
// --
BOOL CALLBACK enumJSCallback(
const DIDEVICEINSTANCE* pDDev, void* pAny
) {
DI_ENUM_CONTEXT* pEnCon = (DI_ENUM_CONTEXT*)pAny;
if(
pEnCon->bJoyCfgValid
&& !IsEqualGUID(pDDev->guidInstance, pEnCon->pJoyCfg->guidInstance)
) return DIENUM_CONTINUE;
HRESULT hr;
if(FAILED(
hr = (*(pEnCon->g_pDI))->CreateDevice( pDDev->guidInstance, pEnCon->g_pJoystick, NULL )
)) return DIENUM_CONTINUE;
return DIENUM_STOP;
}
BOOL CALLBACK enumObjCallback(
const DIDEVICEOBJECTINSTANCE* pdidoi, void* pAny
){
DI_ENUM_CONTEXT* pEnCon = (DI_ENUM_CONTEXT*)pAny;
if( pdidoi->dwType & DIDFT_AXIS )
{
DIPROPRANGE diprg;
diprg.diph.dwSize = sizeof( DIPROPRANGE );
diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER );
diprg.diph.dwHow = DIPH_BYID;
diprg.diph.dwObj = pdidoi->dwType;
diprg.lMin = -1000;
diprg.lMax = +1000;
if( FAILED( (*(pEnCon->g_pJoystick))->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) return DIENUM_STOP;
}
static unsigned int FLG = 0; // TODO: 現在放置
if( pdidoi->guidType == GUID_XAxis ) FLG |= 1;
if( pdidoi->guidType == GUID_YAxis ) FLG |= 2;
if( pdidoi->guidType == GUID_ZAxis ) FLG |= 4;
if( pdidoi->guidType == GUID_RxAxis ) FLG |= 8;
if( pdidoi->guidType == GUID_RyAxis ) FLG |= 16;
if( pdidoi->guidType == GUID_RzAxis ) FLG |= 32;
return DIENUM_CONTINUE;
}
// --------------------------------------------------------------------------------
// --
JoyStick::JoyStick(HWND hWnd)
{
pjc = new _P_JoyCtrl(hWnd);
}
JoyStick::~JoyStick()
{
delete reinterpret_cast<_P_JoyCtrl*>(pjc);
}
void JoyStick::start()
{
reinterpret_cast<_P_JoyCtrl*>(pjc)->start();
}