| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
- //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
- #include "Input/BsGamepad.h"
- #include "Input/BsInput.h"
- #include "Win32/BsWin32Input.h"
- #include "Error/BsException.h"
- #undef DIJOFS_BUTTON
- #define DIJOFS_BUTTON(n) (FIELD_OFFSET(DIJOYSTATE2, rgbButtons) + (n))
- namespace bs
- {
- /** Contains state of a POV (DPad). */
- struct POVState
- {
- ButtonCode code;
- bool pressed;
- };
- /** Contains private data for the Win32 Gamepad implementation. */
- struct Gamepad::Pimpl
- {
- IDirectInput8* directInput;
- IDirectInputDevice8* gamepad;
- GamepadInfo info;
- DWORD coopSettings;
- HWND hWnd;
- POVState povState[4];
- INT32 axisState[6]; // Only for XInput
- bool buttonState[16]; // Only for XInput
- };
- /**
- * Initializes DirectInput gamepad device for a window with the specified handle. Only input from that window will be
- * reported.
- */
- void initializeDirectInput(Gamepad::Pimpl* m, HWND hWnd)
- {
- DIPROPDWORD dipdw;
- dipdw.diph.dwSize = sizeof(DIPROPDWORD);
- dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
- dipdw.diph.dwObj = 0;
- dipdw.diph.dwHow = DIPH_DEVICE;
- dipdw.dwData = DI_BUFFER_SIZE_GAMEPAD;
- if (FAILED(m->directInput->CreateDevice(m->info.guidInstance, &m->gamepad, nullptr)))
- BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to create device.");
- if (FAILED(m->gamepad->SetDataFormat(&c_dfDIJoystick2)))
- BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set format.");
- if (FAILED(m->gamepad->SetCooperativeLevel(hWnd, m->coopSettings)))
- BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set coop level.");
- if (FAILED(m->gamepad->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph)))
- BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to set property.");
- HRESULT hr = m->gamepad->Acquire();
- if (FAILED(hr) && hr != DIERR_OTHERAPPHASPRIO)
- BS_EXCEPT(InternalErrorException, "DirectInput gamepad init: Failed to acquire device.");
- m->hWnd = hWnd;
- }
- /** Releases DirectInput resources for the provided device */
- void releaseDirectInput(Gamepad::Pimpl* m)
- {
- if(m->gamepad)
- {
- m->gamepad->Unacquire();
- m->gamepad->Release();
- m->gamepad = nullptr;
- }
- }
- /** Handles a DirectInput POV event. */
- void handlePOV(Input* owner, Gamepad::Pimpl* m, int pov, DIDEVICEOBJECTDATA& di)
- {
- if (LOWORD(di.dwData) == 0xFFFF)
- {
- // Centered, release any buttons
- if (m->povState[pov].pressed)
- {
- owner->_notifyButtonReleased(m->info.id, m->povState[pov].code, di.dwTimeStamp);
- m->povState[pov].pressed = false;
- }
- }
- else
- {
- POVState newPOVState;
- bs_zero_out(newPOVState);
- switch (di.dwData)
- {
- case 0:
- newPOVState.code = BC_GAMEPAD_DPAD_UP;
- newPOVState.pressed = true;
- break;
- case 4500:
- newPOVState.code = BC_GAMEPAD_DPAD_UPRIGHT;
- newPOVState.pressed = true;
- break;
- case 9000:
- newPOVState.code = BC_GAMEPAD_DPAD_RIGHT;
- newPOVState.pressed = true;
- break;
- case 13500:
- newPOVState.code = BC_GAMEPAD_DPAD_DOWNRIGHT;
- newPOVState.pressed = true;
- break;
- case 18000:
- newPOVState.code = BC_GAMEPAD_DPAD_DOWN;
- newPOVState.pressed = true;
- break;
- case 22500:
- newPOVState.code = BC_GAMEPAD_DPAD_DOWNLEFT;
- newPOVState.pressed = true;
- break;
- case 27000:
- newPOVState.code = BC_GAMEPAD_DPAD_LEFT;
- newPOVState.pressed = true;
- break;
- case 31500:
- newPOVState.code = BC_GAMEPAD_DPAD_UPLEFT;
- newPOVState.pressed = true;
- break;
- }
- // Button was pressed
- if (newPOVState.pressed)
- {
- // Another button was previously pressed
- if (m->povState[pov].pressed)
- {
- // If its a different button, release the old one and press the new one
- if (m->povState[pov].code != newPOVState.code)
- {
- owner->_notifyButtonReleased(m->info.id, m->povState[pov].code, di.dwTimeStamp);
- owner->_notifyButtonPressed(m->info.id, newPOVState.code, di.dwTimeStamp);
- m->povState[pov].code = newPOVState.code;
- }
- }
- else
- {
- owner->_notifyButtonPressed(m->info.id, newPOVState.code, di.dwTimeStamp);
- m->povState[pov].code = newPOVState.code;
- m->povState[pov].pressed = true;
- }
- }
- }
- }
- /** Converts a DirectInput or XInput button code to Banshee ButtonCode. */
- ButtonCode gamepadButtonToButtonCode(INT32 code)
- {
- switch (code)
- {
- case 0:
- return BC_GAMEPAD_DPAD_UP;
- case 1:
- return BC_GAMEPAD_DPAD_DOWN;
- case 2:
- return BC_GAMEPAD_DPAD_LEFT;
- case 3:
- return BC_GAMEPAD_DPAD_RIGHT;
- case 4:
- return BC_GAMEPAD_START;
- case 5:
- return BC_GAMEPAD_BACK;
- case 6:
- return BC_GAMEPAD_LS;
- case 7:
- return BC_GAMEPAD_RS;
- case 8:
- return BC_GAMEPAD_LB;
- case 9:
- return BC_GAMEPAD_RB;
- case 10:
- return BC_GAMEPAD_BTN1;
- case 11:
- return BC_GAMEPAD_LS;
- case 12:
- return BC_GAMEPAD_A;
- case 13:
- return BC_GAMEPAD_B;
- case 14:
- return BC_GAMEPAD_X;
- case 15:
- return BC_GAMEPAD_Y;
- }
- return (ButtonCode)(BC_GAMEPAD_BTN1 + (code - 15));
- }
- Gamepad::Gamepad(const String& name, const GamepadInfo& gamepadInfo, Input* owner)
- : mName(name), mOwner(owner)
- {
- InputPrivateData* pvtData = owner->_getPrivateData();
- m = bs_new<Pimpl>();
- m->directInput = pvtData->directInput;
- m->coopSettings = pvtData->mouseSettings;
- m->info = gamepadInfo;
- m->gamepad = nullptr;
- m->hWnd = (HWND)owner->_getWindowHandle();
- bs_zero_out(m->povState);
- bs_zero_out(m->axisState);
- bs_zero_out(m->buttonState);
- if(!m->info.isXInput)
- initializeDirectInput(m, m->hWnd);
- }
- Gamepad::~Gamepad()
- {
- releaseDirectInput(m);
- bs_delete(m);
- }
- void Gamepad::capture()
- {
- if (m->hWnd == (HWND)-1 || m->gamepad == nullptr)
- return;
- if(m->info.isXInput)
- {
- XINPUT_STATE inputState;
- if (XInputGetState((DWORD)m->info.xInputDev, &inputState) != ERROR_SUCCESS)
- memset(&inputState, 0, sizeof(inputState));
- // Sticks and triggers
- struct AxisState
- {
- bool moved;
- INT32 value;
- };
- AxisState axisState[6];
- bs_zero_out(axisState);
- // Note: Order of axes must match InputAxis enum
- // Left stick
- axisState[0].value = (int)inputState.Gamepad.sThumbLX;
- axisState[1].value = -(int)inputState.Gamepad.sThumbLY;
- // Right stick
- axisState[2].value = (int)inputState.Gamepad.sThumbRX;
- axisState[3].value = -(int)inputState.Gamepad.sThumbRY;
- // Left trigger
- axisState[4].value = std::min((int)inputState.Gamepad.bLeftTrigger * 129, MAX_AXIS);
- // Right trigger
- axisState[5].value = std::min((int)inputState.Gamepad.bRightTrigger * 129, MAX_AXIS);
- for (UINT32 i = 0; i < 6; i++)
- {
- axisState[i].moved = axisState[i].value != m->axisState[i];
- m->axisState[i] = axisState[i].value;
- }
- // DPAD (POV)
- ButtonCode dpadButton = BC_UNASSIGNED;
- if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) != 0)
- dpadButton = BC_GAMEPAD_DPAD_UP;
- else if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) != 0)
- dpadButton = BC_GAMEPAD_DPAD_DOWN;
- if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) != 0 )
- dpadButton = BC_GAMEPAD_DPAD_LEFT;
- else if ((inputState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) != 0)
- dpadButton = BC_GAMEPAD_DPAD_RIGHT;
- if(dpadButton != BC_UNASSIGNED) // Pressed
- {
- // Another button was previously pressed
- if (m->povState[0].pressed)
- {
- // If its a different button, release the old one and press the new one
- if (m->povState[0].code != dpadButton)
- {
- mOwner->_notifyButtonReleased(m->info.id, m->povState[0].code, GetTickCount64());
- mOwner->_notifyButtonPressed(m->info.id, dpadButton, GetTickCount64());
- m->povState[0].code = dpadButton;
- }
- }
- else
- {
- mOwner->_notifyButtonPressed(m->info.id, dpadButton, GetTickCount64());
- m->povState[0].code = dpadButton;
- m->povState[0].pressed = true;
- }
- }
- else
- {
- if (m->povState[0].pressed)
- {
- mOwner->_notifyButtonReleased(m->info.id, m->povState[0].code, GetTickCount64());
- m->povState[0].pressed = false;
- }
- }
- // Buttons
- for (UINT32 i = 0; i < 16; i++)
- {
- bool buttonState = (inputState.Gamepad.wButtons & (1 << i)) != 0;
- if(buttonState != m->buttonState[i])
- {
- if (buttonState)
- mOwner->_notifyButtonPressed(m->info.id, gamepadButtonToButtonCode(i), GetTickCount64());
- else
- mOwner->_notifyButtonReleased(m->info.id, gamepadButtonToButtonCode(i), GetTickCount64());
- m->buttonState[i] = buttonState;
- }
- }
- for (int i = 0; i < 6; ++i)
- {
- if (!axisState[i].moved)
- continue;
- mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
- }
- }
- else // DirectInput
- {
- DIDEVICEOBJECTDATA diBuff[DI_BUFFER_SIZE_GAMEPAD];
- DWORD numEntries = DI_BUFFER_SIZE_GAMEPAD;
- HRESULT hr = m->gamepad->Poll();
- if (hr == DI_OK)
- hr = m->gamepad->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
- if (hr != DI_OK)
- {
- hr = m->gamepad->Acquire();
- while (hr == DIERR_INPUTLOST)
- hr = m->gamepad->Acquire();
- m->gamepad->Poll();
- hr = m->gamepad->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), diBuff, &numEntries, 0);
- if (FAILED(hr))
- return;
- }
- struct AxisState
- {
- bool moved;
- INT32 value;
- };
- AxisState axisState[24];
- bs_zero_out(axisState);
- // Note: Not reporting slider or POV events
- for (UINT32 i = 0; i < numEntries; ++i)
- {
- switch (diBuff[i].dwOfs)
- {
- case DIJOFS_POV(0):
- handlePOV(mOwner, m, 0, diBuff[i]);
- break;
- case DIJOFS_POV(1):
- handlePOV(mOwner, m, 1, diBuff[i]);
- break;
- case DIJOFS_POV(2):
- handlePOV(mOwner, m, 2, diBuff[i]);
- break;
- case DIJOFS_POV(3):
- handlePOV(mOwner, m, 3, diBuff[i]);
- break;
- default:
- // Button event
- if (diBuff[i].dwOfs >= DIJOFS_BUTTON(0) && diBuff[i].dwOfs < DIJOFS_BUTTON(128))
- {
- int button = diBuff[i].dwOfs - DIJOFS_BUTTON(0);
- if ((diBuff[i].dwData & 0x80) != 0)
- mOwner->_notifyButtonPressed(m->info.id, gamepadButtonToButtonCode(button), diBuff[i].dwTimeStamp);
- else
- mOwner->_notifyButtonReleased(m->info.id, gamepadButtonToButtonCode(button), diBuff[i].dwTimeStamp);
- }
- else if ((short)(diBuff[i].uAppData >> 16) == 0x1313) // Axis event
- {
- int axis = (int)(0x0000FFFF & diBuff[i].uAppData);
- if (axis < 24)
- {
- axisState[axis].moved = true;
- axisState[axis].value = diBuff[i].dwData;
- }
- }
- }
- }
- if (numEntries > 0)
- {
- for (int i = 0; i < 24; ++i)
- {
- if (!axisState[i].moved)
- continue;
- mOwner->_notifyAxisMoved(m->info.id, i + (int)InputAxis::MouseZ, axisState[i].value);
- }
- }
- }
- }
- void Gamepad::changeCaptureContext(UINT64 windowHandle)
- {
- HWND newhWnd = (HWND)windowHandle;
- if(m->hWnd != newhWnd)
- {
- releaseDirectInput(m);
- if (!m->info.isXInput && windowHandle != (UINT64)-1)
- initializeDirectInput(m, newhWnd);
- else
- m->hWnd = newhWnd;
- }
- }
- }
|