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.
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)
55EXTERN_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));
78vrpn_DirectXRumblePad::vrpn_DirectXRumblePad(
const char *name,
vrpn_Connection *c,
83 , _target_device(device_guid)
85 , _thread(INVALID_HANDLE_VALUE)
90 last_error = time(NULL);
97 if (register_autodeleted_handler(request_m_id,
98 handle_request_message,
this, d_sender_id)) {
99 FAIL(
"vrpn_DirectXRumblePad: can't register change channel request handler");
104 if (register_autodeleted_handler(request_channels_m_id,
105 handle_request_channels_message,
this, d_sender_id)) {
106 FAIL(
"vrpn_DirectXRumblePad: can't register change channels request handler");
111 if (register_autodeleted_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.");
126vrpn_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);
145BOOL 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;
172DWORD 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();
273LRESULT 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);
342BOOL 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;
362void 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);
404void vrpn_DirectXRumblePad::report(vrpn_uint32 class_of_service) {
412void vrpn_DirectXRumblePad::report_changes(vrpn_uint32 class_of_service) {
421int 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);
454int 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);
498HRESULT 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);
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
virtual void report_changes(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report only if something has changed (for servers) Optionally, tell what time to stamp the val...
Generic connection class not specific to the transport mechanism.
This structure is what is passed to a vrpn_Connection message callback.
const char * vrpn_dropped_last_connection
VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
Utility routine for taking a struct timeval from a buffer that was sent as a message.
#define vrpn_gettimeofday