//////////////////////////////////////////////////////////// // // SFML - Simple and Fast Multimedia Library // Copyright (C) 2007-2016 Laurent Gomila (laurent@sfml-dev.org) // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it freely, // subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; // you must not claim that you wrote the original software. // If you use this software in a product, an acknowledgment // in the product documentation would be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, // and must not be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include namespace { struct ConnectionCache { ConnectionCache() : connected(false) {} bool connected; sf::Clock timer; }; const sf::Time connectionRefreshDelay = sf::milliseconds(500); ConnectionCache connectionCache[sf::Joystick::Count]; // Get a system error string from an error code std::string getErrorString(DWORD error) { PTCHAR buffer; if (FormatMessage(FORMAT_MESSAGE_MAX_WIDTH_MASK | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, reinterpret_cast(&buffer), 0, NULL) == 0) return "Unknown error."; sf::String message = buffer; LocalFree(buffer); return message.toAnsiString(); } // Get the joystick's name sf::String getDeviceName(unsigned int index, JOYCAPS caps) { // Give the joystick a default name sf::String joystickDescription = "Unknown Joystick"; LONG result; HKEY rootKey; HKEY currentKey; std::basic_string subkey; subkey = REGSTR_PATH_JOYCONFIG; subkey += TEXT('\\'); subkey += caps.szRegKey; subkey += TEXT('\\'); subkey += REGSTR_KEY_JOYCURR; rootKey = HKEY_CURRENT_USER; result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, ¤tKey); if (result != ERROR_SUCCESS) { rootKey = HKEY_LOCAL_MACHINE; result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, ¤tKey); if (result != ERROR_SUCCESS) { sf::err() << "Unable to open registry for joystick at index " << index << ": " << getErrorString(result) << std::endl; return joystickDescription; } } std::basic_ostringstream indexString; indexString << index + 1; subkey = TEXT("Joystick"); subkey += indexString.str(); subkey += REGSTR_VAL_JOYOEMNAME; TCHAR keyData[256]; DWORD keyDataSize = sizeof(keyData); result = RegQueryValueEx(currentKey, subkey.c_str(), NULL, NULL, reinterpret_cast(keyData), &keyDataSize); RegCloseKey(currentKey); if (result != ERROR_SUCCESS) { sf::err() << "Unable to query registry key for joystick at index " << index << ": " << getErrorString(result) << std::endl; return joystickDescription; } subkey = REGSTR_PATH_JOYOEM; subkey += TEXT('\\'); subkey.append(keyData, keyDataSize / sizeof(TCHAR)); result = RegOpenKeyEx(rootKey, subkey.c_str(), 0, KEY_READ, ¤tKey); if (result != ERROR_SUCCESS) { sf::err() << "Unable to open registry key for joystick at index " << index << ": " << getErrorString(result) << std::endl; return joystickDescription; } keyDataSize = sizeof(keyData); result = RegQueryValueEx(currentKey, REGSTR_VAL_JOYOEMNAME, NULL, NULL, reinterpret_cast(keyData), &keyDataSize); RegCloseKey(currentKey); if (result != ERROR_SUCCESS) { sf::err() << "Unable to query name for joystick at index " << index << ": " << getErrorString(result) << std::endl; return joystickDescription; } keyData[255] = TEXT('\0'); // Ensure null terminator in case the data is too long. joystickDescription = keyData; return joystickDescription; } } namespace sf { namespace priv { //////////////////////////////////////////////////////////// void JoystickImpl::initialize() { // Perform the initial scan and populate the connection cache for (unsigned int i = 0; i < Joystick::Count; ++i) { ConnectionCache& cache = connectionCache[i]; // Check if the joystick is connected JOYINFOEX joyInfo; joyInfo.dwSize = sizeof(joyInfo); joyInfo.dwFlags = 0; cache.connected = joyGetPosEx(JOYSTICKID1 + i, &joyInfo) == JOYERR_NOERROR; // start the timeout cache.timer.restart(); } } //////////////////////////////////////////////////////////// void JoystickImpl::cleanup() { // Nothing to do } //////////////////////////////////////////////////////////// bool JoystickImpl::isConnected(unsigned int index) { // We check the connection state of joysticks only every N milliseconds, // because of a strange (buggy?) behavior of joyGetPosEx when joysticks // are just plugged/unplugged -- it takes really long and kills the app performances ConnectionCache& cache = connectionCache[index]; if (cache.timer.getElapsedTime() > connectionRefreshDelay) { cache.timer.restart(); JOYINFOEX joyInfo; joyInfo.dwSize = sizeof(joyInfo); joyInfo.dwFlags = 0; cache.connected = joyGetPosEx(JOYSTICKID1 + index, &joyInfo) == JOYERR_NOERROR; return cache.connected; } else { return cache.connected; } } //////////////////////////////////////////////////////////// bool JoystickImpl::open(unsigned int index) { // No explicit "open" action is required m_index = JOYSTICKID1 + index; // Store the joystick capabilities bool success = joyGetDevCaps(m_index, &m_caps, sizeof(m_caps)) == JOYERR_NOERROR; if (success) { m_identification.name = getDeviceName(m_index, m_caps); m_identification.productId = m_caps.wPid; m_identification.vendorId = m_caps.wMid; } return success; } //////////////////////////////////////////////////////////// void JoystickImpl::close() { // Nothing to do } //////////////////////////////////////////////////////////// JoystickCaps JoystickImpl::getCapabilities() const { JoystickCaps caps; caps.buttonCount = m_caps.wNumButtons; if (caps.buttonCount > Joystick::ButtonCount) caps.buttonCount = Joystick::ButtonCount; caps.axes[Joystick::X] = true; caps.axes[Joystick::Y] = true; caps.axes[Joystick::Z] = (m_caps.wCaps & JOYCAPS_HASZ) != 0; caps.axes[Joystick::R] = (m_caps.wCaps & JOYCAPS_HASR) != 0; caps.axes[Joystick::U] = (m_caps.wCaps & JOYCAPS_HASU) != 0; caps.axes[Joystick::V] = (m_caps.wCaps & JOYCAPS_HASV) != 0; caps.axes[Joystick::PovX] = (m_caps.wCaps & JOYCAPS_HASPOV) != 0; caps.axes[Joystick::PovY] = (m_caps.wCaps & JOYCAPS_HASPOV) != 0; return caps; } //////////////////////////////////////////////////////////// Joystick::Identification JoystickImpl::getIdentification() const { return m_identification; } //////////////////////////////////////////////////////////// JoystickState JoystickImpl::update() { JoystickState state; // Get the current joystick state JOYINFOEX pos; pos.dwFlags = JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | JOY_RETURNBUTTONS; pos.dwFlags |= (m_caps.wCaps & JOYCAPS_POVCTS) ? JOY_RETURNPOVCTS : JOY_RETURNPOV; pos.dwSize = sizeof(JOYINFOEX); if (joyGetPosEx(m_index, &pos) == JOYERR_NOERROR) { // The joystick is connected state.connected = true; // Axes state.axes[Joystick::X] = (pos.dwXpos - (m_caps.wXmax + m_caps.wXmin) / 2.f) * 200.f / (m_caps.wXmax - m_caps.wXmin); state.axes[Joystick::Y] = (pos.dwYpos - (m_caps.wYmax + m_caps.wYmin) / 2.f) * 200.f / (m_caps.wYmax - m_caps.wYmin); state.axes[Joystick::Z] = (pos.dwZpos - (m_caps.wZmax + m_caps.wZmin) / 2.f) * 200.f / (m_caps.wZmax - m_caps.wZmin); state.axes[Joystick::R] = (pos.dwRpos - (m_caps.wRmax + m_caps.wRmin) / 2.f) * 200.f / (m_caps.wRmax - m_caps.wRmin); state.axes[Joystick::U] = (pos.dwUpos - (m_caps.wUmax + m_caps.wUmin) / 2.f) * 200.f / (m_caps.wUmax - m_caps.wUmin); state.axes[Joystick::V] = (pos.dwVpos - (m_caps.wVmax + m_caps.wVmin) / 2.f) * 200.f / (m_caps.wVmax - m_caps.wVmin); // Special case for POV, it is given as an angle if (pos.dwPOV != 0xFFFF) { float angle = pos.dwPOV / 18000.f * 3.141592654f; state.axes[Joystick::PovX] = std::sin(angle) * 100; state.axes[Joystick::PovY] = std::cos(angle) * 100; } else { state.axes[Joystick::PovX] = 0; state.axes[Joystick::PovY] = 0; } // Buttons for (unsigned int i = 0; i < Joystick::ButtonCount; ++i) state.buttons[i] = (pos.dwButtons & (1 << i)) != 0; } return state; } } // namespace priv } // namespace sf