14 #if defined(_WIN32) && defined(VRPN_USE_DIRECTINPUT)
23 # if !defined(GetWindowLongPtr) || !defined(SetWindowLongPtr)
24 # error 64-bit compilation requires an SDK that supports LONG_PTRs.
28 # define LONG_PTR LONG
30 # ifdef GetWindowLongPtr
31 # undef GetWindowLongPtr
34 # ifdef SetWindowLongPtr
35 # undef SetWindowLongPtr
37 # define GetWindowLongPtr GetWindowLong
38 # define SetWindowLongPtr SetWindowLong
39 # define GWLP_USERDATA GWL_USERDATA
42 # define HWND_MESSAGE ((HWND) -3)
55 EXTERN_C IMAGE_DOS_HEADER __ImageBase;
56 #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)
59 const char *CLASS_NAME =
"vrpn_DirectXRumblePad";
60 const int MSG_POLL = WM_APP + 101;
61 const int MSG_SETMAGNITUDE = WM_APP + 102;
62 const int POLL_SUCCESS = 1701;
67 inline bool guid_equals(
const GUID &a,
const GUID &b) {
68 return !memcmp(&a, &b,
sizeof(GUID));
78 vrpn_DirectXRumblePad::vrpn_DirectXRumblePad(
const char *name,
vrpn_Connection *c,
83 , _target_device(device_guid)
85 , _thread(INVALID_HANDLE_VALUE)
90 last_error = time(NULL);
99 FAIL(
"vrpn_DirectXRumblePad: can't register change channel request handler");
105 handle_request_channels_message,
this,
d_sender_id)) {
106 FAIL(
"vrpn_DirectXRumblePad: can't register change channels request handler");
113 handle_last_connection_dropped,
this)) {
114 FAIL(
"Can't register self-destruct handler");
118 _thread = CreateThread(NULL, 0, thread_proc,
this, 0, NULL);
119 if (_thread == INVALID_HANDLE_VALUE) {
120 FAIL(
"Unable to create thread.");
126 vrpn_DirectXRumblePad::~vrpn_DirectXRumblePad() {
129 SendMessage(_wnd, WM_CLOSE, 0, 0);
134 if (_thread != INVALID_HANDLE_VALUE) {
137 GetExitCodeThread(_thread, &code);
138 }
while (code == STILL_ACTIVE);
145 BOOL CALLBACK vrpn_DirectXRumblePad::joystick_enum_cb(LPCDIDEVICEINSTANCE lpddi, LPVOID ref) {
146 vrpn_DirectXRumblePad *me =
reinterpret_cast<vrpn_DirectXRumblePad *
>(ref);
148 if (guid_equals(me->_target_device, GUID_NULL)
149 || guid_equals(me->_target_device, lpddi->guidInstance)) {
150 if (SUCCEEDED(me->_directInput->CreateDevice(lpddi->guidInstance, &me->_gamepad, 0))) {
154 me->FAIL(
"Unable to create device instance! Attempting to find another one.");
155 return DIENUM_CONTINUE;
158 return DIENUM_CONTINUE;
172 DWORD CALLBACK vrpn_DirectXRumblePad::thread_proc(LPVOID ref) {
174 vrpn_DirectXRumblePad *me =
reinterpret_cast<vrpn_DirectXRumblePad *
>(ref);
178 wc.lpfnWndProc = window_proc;
179 wc.cbWndExtra =
sizeof(vrpn_DirectXRumblePad *);
180 wc.hInstance = HINST_THISCOMPONENT;
181 wc.lpszClassName = CLASS_NAME;
183 if (!RegisterClass(&wc)) {
184 me->FAIL(
"Unable to register class.");
189 me->_wnd = CreateWindow(CLASS_NAME,
"VRPN Worker Thread", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, ref);
192 me->FAIL(
"Unable to create window.");
198 IID_IDirectInput8, (
void**)&me->_directInput, NULL))) {
199 me->FAIL(
"Unable to connect DirectInput.");
204 if (FAILED(me->_directInput->EnumDevices(DI8DEVCLASS_GAMECTRL,
205 joystick_enum_cb, me, DIEDFL_ATTACHEDONLY | DIEDFL_FORCEFEEDBACK))) {
206 me->FAIL(
"Unable to enumerate joysticks.");
212 me->FAIL(
"No compatible joystick found!");
217 if (FAILED(me->_gamepad->SetDataFormat(&c_dfDIJoystick2))) {
218 me->FAIL(
"Unable to set data format.");
223 if (FAILED(me->_gamepad->SetCooperativeLevel(me->_wnd, DISCL_EXCLUSIVE | DISCL_BACKGROUND))) {
224 me->FAIL(
"Unable to set cooperative level.");
229 if (FAILED(me->_gamepad->EnumObjects(axis_enum_cb, me->_gamepad, DIDFT_AXIS))) {
230 me->FAIL(
"Unable to enumerate axes.");
235 if (FAILED(me->init_force())) {
236 me->FAIL(
"Unable to initialize forces.");
241 if (FAILED(me->_gamepad->Acquire())) {
242 me->FAIL(
"Unable to acquire joystick.");
249 while ((ret = GetMessage(&msg, me->_wnd, 0, 0)) != 0) {
251 me->FAIL(
"GetMessage() threw an error.");
255 TranslateMessage(&msg);
256 DispatchMessage(&msg);
262 me->_effect->Release();
264 me->_gamepad->Release();
265 if (me->_directInput)
266 me->_directInput->Release();
273 LRESULT CALLBACK vrpn_DirectXRumblePad::window_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
277 vrpn_DirectXRumblePad *me =
reinterpret_cast<vrpn_DirectXRumblePad *
>
278 (GetWindowLongPtr(hwnd, GWLP_USERDATA));
283 CREATESTRUCT *s =
reinterpret_cast<CREATESTRUCT *
>(lp);
284 me =
reinterpret_cast<vrpn_DirectXRumblePad *
>(s->lpCreateParams);
285 SetWindowLongPtr(hwnd, GWLP_USERDATA,
reinterpret_cast<LONG_PTR
>(me));
298 if (FAILED(me->_gamepad->Poll())) {
301 me->_gamepad->Acquire();
302 }
while (me->_gamepad->Poll() == DIERR_INPUTLOST);
305 if (SUCCEEDED(me->_gamepad->GetDeviceState(
sizeof(DIJOYSTATE2),
reinterpret_cast<DIJOYSTATE2 *
>(lp)))) {
309 me->FAIL(
"GetDeviceState() returned error result.");
314 case MSG_SETMAGNITUDE: {
315 float mag = *
reinterpret_cast<float*
>(&wp);
323 me->_diPeriodic.dwMagnitude = (DWORD) (DI_FFNOMINALMAX * mag);
324 hr = me->_effect->SetParameters(&me->_diEffect, DIEP_TYPESPECIFICPARAMS);
325 hr = me->_effect->Download();
326 hr = me->_effect->Start(1, 0);
334 return DefWindowProc(hwnd, msg, wp, lp);
342 BOOL CALLBACK vrpn_DirectXRumblePad::axis_enum_cb(LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID ref) {
343 LPDIRECTINPUTDEVICE8 gamepad =
reinterpret_cast<LPDIRECTINPUTDEVICE8
>(ref);
346 prop.diph.dwSize =
sizeof(DIPROPRANGE);
347 prop.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
348 prop.diph.dwHow = DIPH_BYID;
349 prop.diph.dwObj = lpddoi->dwType;
353 if (FAILED(gamepad->SetProperty(DIPROP_RANGE, &prop.diph))) {
357 return DIENUM_CONTINUE;
362 void vrpn_DirectXRumblePad::mainloop() {
367 if (SendMessage(_wnd, MSG_POLL, 0,
reinterpret_cast<LPARAM
>(&js)) != POLL_SUCCESS) {
368 if ((time(NULL) - last_error) > 2) {
378 channel[0] = js.lX / 1000.0;
379 channel[1] = js.lY / 1000.0;
380 channel[2] = js.lZ / 1000.0;
382 channel[3] = js.lRx / 1000.0;
383 channel[4] = js.lRy / 1000.0;
384 channel[5] = js.lRz / 1000.0;
386 channel[6] = js.rglSlider[0] / 1000.0;
387 channel[7] = js.rglSlider[1] / 1000.0;
390 for (i = 0; i < 4; i++) {
391 long v = (long) js.rgdwPOV[i];
392 channel[8+i] = (v == -1) ? -1 : (v / 100.0);
396 buttons[i] = ( (js.rgbButtons[i] & 0x80) != 0);
404 void vrpn_DirectXRumblePad::report(vrpn_uint32 class_of_service) {
412 void vrpn_DirectXRumblePad::report_changes(vrpn_uint32 class_of_service) {
421 int vrpn_DirectXRumblePad::handle_request_message(
void *userdata,
424 const char* bufptr = p.
buffer;
428 vrpn_DirectXRumblePad* me = (vrpn_DirectXRumblePad*)userdata;
437 if ( (chan_num < 0) || (chan_num >= me->o_num_channel) ) {
438 fprintf(stderr,
"vrpn_Analog_Output_Server::handle_request_message(): Index out of bounds\n");
440 sprintf( msg,
"Error: (handle_request_message): channel %d is not active. Squelching.", chan_num );
444 me->o_channel[chan_num] = value;
446 float mag =
static_cast<float>(value);
447 mag = (mag < 0) ? 0 : (mag > 1) ? 1 : mag;
448 SendMessage(me->_wnd, MSG_SETMAGNITUDE, *
reinterpret_cast<WPARAM*
>(&mag), 0);
454 int vrpn_DirectXRumblePad::handle_request_channels_message(
void* userdata,
457 const char* bufptr = p.
buffer;
460 vrpn_DirectXRumblePad* me = (vrpn_DirectXRumblePad*)userdata;
465 if (num > me->o_num_channel)
468 sprintf( msg,
"Error: (handle_request_channels_message): channels above %d not active; "
469 "bad request up to channel %d. Squelching.", me->o_num_channel, num );
471 num = me->o_num_channel;
476 sprintf( msg,
"Error: (handle_request_channels_message): invalid channel %d. Squelching.", num );
484 float mag =
static_cast<float>(value);
485 mag = (mag < 0) ? 0 : (mag > 1) ? 1 : mag;
486 SendMessage(me->_wnd, MSG_SETMAGNITUDE, *
reinterpret_cast<WPARAM*
>(&mag), 0);
493 SendMessage(((vrpn_DirectXRumblePad *)selfPtr)->_wnd, MSG_SETMAGNITUDE, 0, 0);
498 HRESULT vrpn_DirectXRumblePad::init_force() {
503 prop.diph.dwSize =
sizeof(prop);
504 prop.diph.dwHeaderSize =
sizeof(DIPROPHEADER);
506 prop.diph.dwHow = DIPH_DEVICE;
507 prop.dwData = DIPROPAUTOCENTER_OFF;
509 hr = _gamepad->SetProperty(DIPROP_AUTOCENTER, &prop.diph);
514 DWORD dwAxes[2] = {DIJOFS_X, DIJOFS_Y};
515 LONG lDirection[2] = {0, 0};
517 _diPeriodic.dwMagnitude = DI_FFNOMINALMAX;
518 _diPeriodic.lOffset = 0;
519 _diPeriodic.dwPhase = 0;
520 _diPeriodic.dwPeriod =
static_cast<DWORD
>(0.05 * DI_SECONDS);
522 _diEffect.dwSize =
sizeof(DIEFFECT);
523 _diEffect.dwFlags = DIEFF_POLAR | DIEFF_OBJECTOFFSETS;
524 _diEffect.dwDuration = INFINITE;
525 _diEffect.dwSamplePeriod = 0;
526 _diEffect.dwGain = DI_FFNOMINALMAX;
527 _diEffect.dwTriggerButton = DIEB_NOTRIGGER;
528 _diEffect.dwTriggerRepeatInterval = 0;
530 _diEffect.rgdwAxes = dwAxes;
531 _diEffect.rglDirection = &lDirection[0];
532 _diEffect.lpEnvelope = NULL;
533 _diEffect.cbTypeSpecificParams =
sizeof(_diPeriodic);
534 _diEffect.lpvTypeSpecificParams = &_diPeriodic;
535 _diEffect.dwStartDelay = 0;
537 hr = _gamepad->CreateEffect(GUID_Square, &_diEffect, &_effect, NULL);
543 #endif // _WIN32 and VRPN_USE_DIRECTINPUT