diff --git a/src/SFML/Window/Win32/JoystickImpl.cpp b/src/SFML/Window/Win32/JoystickImpl.cpp index 2e925a57..9f8339d5 100644 --- a/src/SFML/Window/Win32/JoystickImpl.cpp +++ b/src/SFML/Window/Win32/JoystickImpl.cpp @@ -80,6 +80,17 @@ namespace typedef std::vector JoystickList; JoystickList joystickList; + + struct JoystickBlacklistEntry + { + unsigned int vendorId; + unsigned int productId; + }; + + typedef std::vector JoystickBlacklist; + JoystickBlacklist joystickBlacklist; + + const DWORD directInputEventBufferSize = 32; } @@ -330,7 +341,16 @@ Joystick::Identification JoystickImpl::getIdentification() const JoystickState JoystickImpl::update() { if (directInput) - return updateDInput(); + { + if (m_buffered) + { + return updateDInputBuffered(); + } + else + { + return updateDInputPolled(); + } + } JoystickState state; @@ -391,7 +411,7 @@ void JoystickImpl::initializeDInput() // Try to acquire a DirectInput 8.x interface HRESULT result = directInput8Create(GetModuleHandleW(NULL), 0x0800, guids::IID_IDirectInput8W, reinterpret_cast(&directInput), NULL); - if (result) + if (FAILED(result)) { // De-initialize everything directInput = NULL; @@ -460,7 +480,7 @@ void JoystickImpl::updateConnectionsDInput() ++i; } - if (result) + if (FAILED(result)) { err() << "Failed to enumerate DirectInput devices: " << result << std::endl; @@ -499,6 +519,8 @@ bool JoystickImpl::openDInput(unsigned int index) std::memset(&m_deviceCaps, 0, sizeof(DIDEVCAPS)); m_deviceCaps.dwSize = sizeof(DIDEVCAPS); + m_state = JoystickState(); + m_buffered = false; // Search for a joystick with the given index in the connected list for (std::vector::iterator i = joystickList.begin(); i != joystickList.end(); ++i) @@ -508,13 +530,54 @@ bool JoystickImpl::openDInput(unsigned int index) // Create device HRESULT result = directInput->CreateDevice(i->guid, &m_device, NULL); - if (result) + if (FAILED(result)) { err() << "Failed to create DirectInput device: " << result << std::endl; return false; } + // Get vendor and product id of the device + DIPROPDWORD property; + std::memset(&property, 0, sizeof(property)); + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwHow = DIPH_DEVICE; + + if (SUCCEEDED(m_device->GetProperty(DIPROP_VIDPID, &property.diph))) + { + m_identification.productId = HIWORD(property.dwData); + m_identification.vendorId = LOWORD(property.dwData); + + // Check if device is already blacklisted + if (m_identification.productId && m_identification.vendorId) + { + for (JoystickBlacklist::const_iterator iter = joystickBlacklist.begin(); iter != joystickBlacklist.end(); ++iter) + { + if ((m_identification.productId == iter->productId) && + (m_identification.vendorId == iter->vendorId)) + { + // Device is blacklisted + m_device->Release(); + m_device = NULL; + + return false; + } + } + } + } + + // Get friendly product name of the device + DIPROPSTRING stringProperty; + std::memset(&stringProperty, 0, sizeof(stringProperty)); + stringProperty.diph.dwSize = sizeof(stringProperty); + stringProperty.diph.dwHeaderSize = sizeof(stringProperty.diph); + stringProperty.diph.dwHow = DIPH_DEVICE; + stringProperty.diph.dwObj = 0; + + if (SUCCEEDED(m_device->GetProperty(DIPROP_PRODUCTNAME, &stringProperty.diph))) + m_identification.name = stringProperty.wsz; + static bool formatInitialized = false; static DIDATAFORMAT format; @@ -524,59 +587,80 @@ bool JoystickImpl::openDInput(unsigned int index) const DWORD povType = DIDFT_POV | DIDFT_OPTIONAL | DIDFT_ANYINSTANCE; const DWORD buttonType = DIDFT_BUTTON | DIDFT_OPTIONAL | DIDFT_ANYINSTANCE; - static DIOBJECTDATAFORMAT data[8 + 4 + sf::Joystick::ButtonCount]; + static DIOBJECTDATAFORMAT data[8 * 4 + 4 + sf::Joystick::ButtonCount]; - data[0].pguid = &guids::GUID_XAxis; - data[0].dwOfs = DIJOFS_X; + for (int i = 0; i < 4; ++i) + { + data[8 * i + 0].pguid = &guids::GUID_XAxis; + data[8 * i + 1].pguid = &guids::GUID_YAxis; + data[8 * i + 2].pguid = &guids::GUID_ZAxis; + data[8 * i + 3].pguid = &guids::GUID_RxAxis; + data[8 * i + 4].pguid = &guids::GUID_RyAxis; + data[8 * i + 5].pguid = &guids::GUID_RzAxis; + data[8 * i + 6].pguid = &guids::GUID_Slider; + data[8 * i + 7].pguid = &guids::GUID_Slider; + } - data[1].pguid = &guids::GUID_YAxis; - data[1].dwOfs = DIJOFS_Y; + data[ 0].dwOfs = DIJOFS_X; + data[ 1].dwOfs = DIJOFS_Y; + data[ 2].dwOfs = DIJOFS_Z; + data[ 3].dwOfs = DIJOFS_RX; + data[ 4].dwOfs = DIJOFS_RY; + data[ 5].dwOfs = DIJOFS_RZ; + data[ 6].dwOfs = DIJOFS_SLIDER(0); + data[ 7].dwOfs = DIJOFS_SLIDER(1); + data[ 8].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVX); + data[ 9].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVY); + data[10].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVZ); + data[11].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVRx); + data[12].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVRy); + data[13].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lVRz); + data[14].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglVSlider[0]); + data[15].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglVSlider[1]); + data[16].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lAX); + data[17].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lAY); + data[18].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lAZ); + data[19].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lARx); + data[20].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lARy); + data[21].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lARz); + data[22].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglASlider[0]); + data[23].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglASlider[1]); + data[24].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFX); + data[25].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFY); + data[26].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFZ); + data[27].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFRx); + data[28].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFRy); + data[29].dwOfs = FIELD_OFFSET(DIJOYSTATE2, lFRz); + data[30].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglFSlider[0]); + data[31].dwOfs = FIELD_OFFSET(DIJOYSTATE2, rglFSlider[1]); - data[2].pguid = &guids::GUID_ZAxis; - data[2].dwOfs = DIJOFS_Z; - - data[3].pguid = &guids::GUID_RxAxis; - data[3].dwOfs = DIJOFS_RX; - - data[4].pguid = &guids::GUID_RyAxis; - data[4].dwOfs = DIJOFS_RY; - - data[5].pguid = &guids::GUID_RzAxis; - data[5].dwOfs = DIJOFS_RZ; - - data[6].pguid = &guids::GUID_Slider; - data[6].dwOfs = DIJOFS_SLIDER(0); - - data[7].pguid = &guids::GUID_Slider; - data[7].dwOfs = DIJOFS_SLIDER(1); - - for (int i = 0; i < 8; ++i) + for (int i = 0; i < 8 * 4; ++i) { data[i].dwType = axisType; - data[i].dwFlags = DIDOI_ASPECTPOSITION; + data[i].dwFlags = 0; } for (int i = 0; i < 4; ++i) { - data[8 + i].pguid = &guids::GUID_POV; - data[8 + i].dwOfs = static_cast(DIJOFS_POV(i)); - data[8 + i].dwType = povType; - data[8 + i].dwFlags = 0; + data[8 * 4 + i].pguid = &guids::GUID_POV; + data[8 * 4 + i].dwOfs = static_cast(DIJOFS_POV(i)); + data[8 * 4 + i].dwType = povType; + data[8 * 4 + i].dwFlags = 0; } for (int i = 0; i < sf::Joystick::ButtonCount; ++i) { - data[8 + 4 + i].pguid = NULL; - data[8 + 4 + i].dwOfs = static_cast(DIJOFS_BUTTON(i)); - data[8 + 4 + i].dwType = buttonType; - data[8 + 4 + i].dwFlags = 0; + data[8 * 4 + 4 + i].pguid = NULL; + data[8 * 4 + 4 + i].dwOfs = static_cast(DIJOFS_BUTTON(i)); + data[8 * 4 + 4 + i].dwType = buttonType; + data[8 * 4 + 4 + i].dwFlags = 0; } format.dwSize = sizeof(DIDATAFORMAT); format.dwObjSize = sizeof(DIOBJECTDATAFORMAT); format.dwFlags = DIDFT_ABSAXIS; - format.dwDataSize = sizeof(DIJOYSTATE); - format.dwNumObjs = 8 + 4 + sf::Joystick::ButtonCount; + format.dwDataSize = sizeof(DIJOYSTATE2); + format.dwNumObjs = 8 * 4 + 4 + sf::Joystick::ButtonCount; format.rgodf = data; formatInitialized = true; @@ -585,7 +669,7 @@ bool JoystickImpl::openDInput(unsigned int index) // Set device data format result = m_device->SetDataFormat(&format); - if (result) + if (FAILED(result)) { err() << "Failed to set DirectInput device data format: " << result << std::endl; @@ -598,7 +682,7 @@ bool JoystickImpl::openDInput(unsigned int index) // Get device capabilities result = m_device->GetCapabilities(&m_deviceCaps); - if (result) + if (FAILED(result)) { err() << "Failed to get DirectInput device capabilities: " << result << std::endl; @@ -608,30 +692,10 @@ bool JoystickImpl::openDInput(unsigned int index) return false; } - // Set axis mode to absolute - DIPROPDWORD property; - std::memset(&property, 0, sizeof(property)); - property.diph.dwSize = sizeof(property); - property.diph.dwHeaderSize = sizeof(property.diph); - property.diph.dwHow = DIPH_DEVICE; - property.dwData = DIPROPAXISMODE_ABS; - - result = m_device->SetProperty(DIPROP_AXISMODE, &property.diph); - - if (result) - { - err() << "Failed to set DirectInput device axis mode: " << result << std::endl; - - m_device->Release(); - m_device = NULL; - - return false; - } - // Enumerate device objects (axes/povs/buttons) result = m_device->EnumObjects(&JoystickImpl::deviceObjectEnumerationCallback, this, DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV); - if (result) + if (FAILED(result)) { err() << "Failed to enumerate DirectInput device objects: " << result << std::endl; @@ -641,29 +705,116 @@ bool JoystickImpl::openDInput(unsigned int index) return false; } - // Get friendly product name of the device - DIPROPSTRING stringProperty; - std::memset(&stringProperty, 0, sizeof(stringProperty)); - stringProperty.diph.dwSize = sizeof(stringProperty); - stringProperty.diph.dwHeaderSize = sizeof(stringProperty.diph); - stringProperty.diph.dwHow = DIPH_DEVICE; - stringProperty.diph.dwObj = 0; - - if (!m_device->GetProperty(DIPROP_PRODUCTNAME, &stringProperty.diph)) + // Set device's axis mode to absolute if the device reports having at least one axis + for (int i = 0; i < Joystick::AxisCount; ++i) { - m_identification.name = stringProperty.wsz; + if (m_axes[i] != -1) + { + std::memset(&property, 0, sizeof(property)); + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwHow = DIPH_DEVICE; + property.diph.dwObj = 0; + + result = m_device->GetProperty(DIPROP_AXISMODE, &property.diph); + + if (FAILED(result)) + { + err() << "Failed to get DirectInput device axis mode for device \"" + << m_identification.name.toAnsiString() << "\": " << result << std::endl; + + m_device->Release(); + m_device = NULL; + + return false; + } + + // If the axis mode is already set to absolute we don't need to set it again ourselves + if (property.dwData == DIPROPAXISMODE_ABS) + break; + + std::memset(&property, 0, sizeof(property)); + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwHow = DIPH_DEVICE; + property.dwData = DIPROPAXISMODE_ABS; + + m_device->SetProperty(DIPROP_AXISMODE, &property.diph); + + // Check if the axis mode has been set to absolute + std::memset(&property, 0, sizeof(property)); + property.diph.dwSize = sizeof(property); + property.diph.dwHeaderSize = sizeof(property.diph); + property.diph.dwHow = DIPH_DEVICE; + property.diph.dwObj = 0; + + result = m_device->GetProperty(DIPROP_AXISMODE, &property.diph); + + if (FAILED(result)) + { + err() << "Failed to verify DirectInput device axis mode for device \"" + << m_identification.name.toAnsiString() << "\": " << result << std::endl; + + m_device->Release(); + m_device = NULL; + + return false; + } + + // If the axis mode hasn't been set to absolute fail here and blacklist the device + if (property.dwData != DIPROPAXISMODE_ABS) + { + if (m_identification.vendorId && m_identification.productId) + { + JoystickBlacklistEntry entry; + + entry.vendorId = m_identification.vendorId; + entry.productId = m_identification.productId; + + joystickBlacklist.push_back(entry); + + // Pre-C++11 shrink_to_fit() + JoystickBlacklist(joystickBlacklist.begin(), joystickBlacklist.end()).swap(joystickBlacklist); + } + + m_device->Release(); + m_device = NULL; + + return false; + } + + break; + } } - // Get vendor and produce id of the device + // Try to enable buffering by setting the buffer size std::memset(&property, 0, sizeof(property)); property.diph.dwSize = sizeof(property); property.diph.dwHeaderSize = sizeof(property.diph); property.diph.dwHow = DIPH_DEVICE; + property.dwData = directInputEventBufferSize; - if (!m_device->GetProperty(DIPROP_VIDPID, &property.diph)) + result = m_device->SetProperty(DIPROP_BUFFERSIZE, &property.diph); + + if (result == DI_OK) { - m_identification.productId = HIWORD(property.dwData); - m_identification.vendorId = LOWORD(property.dwData); + // Buffering supported + m_buffered = true; + } + else if (result == DI_POLLEDDEVICE) + { + // Only polling supported + m_buffered = false; + } + else + { + err() << "Failed to set DirectInput device buffer size for device \"" + << m_identification.name.toAnsiString() << "\": " << result << std::endl; + + m_device->Release(); + m_device = NULL; + + return false; } return true; @@ -709,7 +860,113 @@ JoystickCaps JoystickImpl::getCapabilitiesDInput() const //////////////////////////////////////////////////////////// -JoystickState JoystickImpl::updateDInput() +JoystickState JoystickImpl::updateDInputBuffered() +{ + // If we don't make it to the end of this function, mark the device as disconnected + m_state.connected = false; + + if (!m_device) + return m_state; + + DIDEVICEOBJECTDATA events[directInputEventBufferSize]; + DWORD eventCount = directInputEventBufferSize; + + // Try to get the device data + HRESULT result = m_device->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + + // If we have not acquired or have lost the device, attempt to (re-)acquire it and get the device data again + if ((result == DIERR_NOTACQUIRED) || (result == DIERR_INPUTLOST)) + { + m_device->Acquire(); + result = m_device->GetDeviceData(sizeof(DIDEVICEOBJECTDATA), events, &eventCount, 0); + } + + // If we still can't get the device data, assume it has been disconnected + if ((result == DIERR_NOTACQUIRED) || (result == DIERR_INPUTLOST)) + { + m_device->Release(); + m_device = NULL; + + return m_state; + } + + if (FAILED(result)) + { + err() << "Failed to get DirectInput device data: " << result << std::endl; + + return m_state; + } + + // Iterate through all buffered events + for (DWORD i = 0; i < eventCount; ++i) + { + bool eventHandled = false; + + // Get the current state of each axis + for (int j = 0; j < Joystick::AxisCount; ++j) + { + if (m_axes[j] == events[i].dwOfs) + { + if (j == Joystick::PovX) + { + unsigned short value = LOWORD(events[i].dwData); + + if (value != 0xFFFF) + { + float angle = (static_cast(value)) * 3.141592654f / DI_DEGREES / 180.f; + + m_state.axes[j] = std::sin(angle) * 100.f; + } + else + { + m_state.axes[j] = 0; + } + } + else if (j == Joystick::PovY) + { + unsigned short value = LOWORD(events[i].dwData); + + if (value != 0xFFFF) + { + float angle = (static_cast(value)) * 3.141592654f / DI_DEGREES / 180.f; + + m_state.axes[j] = std::cos(angle) * 100.f; + } + else + { + m_state.axes[j] = 0.f; + } + } + else + { + m_state.axes[j] = (static_cast(static_cast(events[i].dwData)) + 0.5f) * 100.f / 32767.5f; + } + + eventHandled = true; + + break; + } + } + + if (eventHandled) + continue; + + // Get the current state of each button + for (int j = 0; j < Joystick::ButtonCount; ++j) + { + if (m_buttons[j] == events[i].dwOfs) + m_state.buttons[j] = (events[i].dwData != 0); + } + } + + m_state.connected = true; + + return m_state; +} + + +//////////////////////////////////////////////////////////// +JoystickState JoystickImpl::updateDInputPolled() { JoystickState state; @@ -718,7 +975,7 @@ JoystickState JoystickImpl::updateDInput() // Poll the device m_device->Poll(); - DIJOYSTATE joystate; + DIJOYSTATE2 joystate; // Try to get the device state HRESULT result = m_device->GetDeviceState(sizeof(joystate), &joystate); @@ -740,7 +997,7 @@ JoystickState JoystickImpl::updateDInput() return state; } - if (result) + if (FAILED(result)) { err() << "Failed to get DirectInput device state: " << result << std::endl; @@ -878,7 +1135,7 @@ BOOL CALLBACK JoystickImpl::deviceObjectEnumerationCallback(const DIDEVICEOBJECT HRESULT result = joystick.m_device->SetProperty(DIPROP_RANGE, &propertyRange.diph); - if (result) + if (result != DI_OK) err() << "Failed to set DirectInput device axis property range: " << result << std::endl; return DIENUM_CONTINUE; diff --git a/src/SFML/Window/Win32/JoystickImpl.hpp b/src/SFML/Window/Win32/JoystickImpl.hpp index 66175058..ffff8c33 100644 --- a/src/SFML/Window/Win32/JoystickImpl.hpp +++ b/src/SFML/Window/Win32/JoystickImpl.hpp @@ -186,12 +186,20 @@ public: JoystickCaps getCapabilitiesDInput() const; //////////////////////////////////////////////////////////// - /// \brief Update the joystick and get its new state (DInput) + /// \brief Update the joystick and get its new state (DInput, Buffered) /// /// \return Joystick state /// //////////////////////////////////////////////////////////// - JoystickState updateDInput(); + JoystickState updateDInputBuffered(); + + //////////////////////////////////////////////////////////// + /// \brief Update the joystick and get its new state (DInput, Polled) + /// + /// \return Joystick state + /// + //////////////////////////////////////////////////////////// + JoystickState updateDInputPolled(); private: @@ -227,6 +235,8 @@ private: int m_axes[Joystick::AxisCount]; //!< Offsets to the bytes containing the axes states, -1 if not available int m_buttons[Joystick::ButtonCount]; //!< Offsets to the bytes containing the button states, -1 if not available Joystick::Identification m_identification; //!< Joystick identification + JoystickState m_state; //!< Buffered joystick state + bool m_buffered; //!< true if the device uses buffering, false if the device uses polling }; } // namespace priv