diff --git a/configure.in b/configure.in index beb8aacd69e6a..e3fcd04db16ee 100644 --- a/configure.in +++ b/configure.in @@ -2153,6 +2153,9 @@ else final_message="$final_message\n Joystick:\tNo" fi +# --enable-joystick is for SDL joysticks, use joystick.h for Linux Joystick API +AC_CHECK_HEADER([linux/joystick.h], AC_DEFINE([HAS_LINUX_JOYSTICK],[1],[Define if we have linux/joystick.h]),) + if test "$use_xrandr" = "yes"; then final_message="$final_message\n XRandR:\tYes" USE_XRANDR=1 diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 0b1c0c62ad09b..1854e296c88a1 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -626,6 +626,8 @@ + + @@ -634,7 +636,8 @@ - + + @@ -1031,13 +1034,17 @@ - + + + + + @@ -2917,4 +2924,4 @@ - \ No newline at end of file + diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index 829f0531d74b9..4e94f4cd013ef 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -2484,9 +2484,6 @@ utils - - input\windows - filesystem @@ -3113,6 +3110,18 @@ cores\dvdplayer\DVDCodecs\Video + + input + + + input + + + input\windows + + + input\windows + @@ -5593,9 +5602,6 @@ utils - - input\windows - filesystem @@ -6112,6 +6118,21 @@ utils + + input + + + input + + + input + + + input\windows + + + input\windows + win32 diff --git a/system/keymaps/joystick.Microsoft.Xbox.360.Controller.xml b/system/keymaps/joystick.Microsoft.Xbox.360.Controller.xml index 4a1fe438a5a8f..0379304460068 100644 --- a/system/keymaps/joystick.Microsoft.Xbox.360.Controller.xml +++ b/system/keymaps/joystick.Microsoft.Xbox.360.Controller.xml @@ -74,6 +74,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -167,6 +168,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -200,6 +202,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -233,6 +236,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -270,6 +274,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -352,6 +357,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -399,6 +405,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -440,6 +447,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -477,6 +485,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -540,6 +549,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -575,6 +585,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -608,6 +619,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -641,6 +653,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -706,6 +719,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -743,6 +757,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -780,6 +795,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -813,6 +829,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -852,6 +869,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -887,6 +905,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -922,6 +941,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -959,6 +979,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -992,6 +1013,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1035,6 +1057,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1070,6 +1093,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1103,6 +1127,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1136,6 +1161,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1169,6 +1195,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1202,6 +1229,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1235,6 +1263,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1270,6 +1299,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1303,6 +1333,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1336,6 +1367,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1369,6 +1401,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1404,6 +1437,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1437,6 +1471,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) @@ -1472,6 +1507,7 @@ + XBMC-Compatible XInput Controller Afterglow Gamepad for Xbox 360 (Controller) Controller (Gamepad F310) Controller (Gamepad for Xbox 360) diff --git a/system/settings/settings.xml b/system/settings/settings.xml index ca2ac561f2d69..3b509153ad4b1 100644 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -2600,9 +2600,12 @@ true - HAS_SDL_JOYSTICK + HAS_JOYSTICK 0 true + + + diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 265881f41135b..e583de25b5908 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -328,10 +328,8 @@ #include "input/windows/IRServerSuite.h" #endif -#if defined(TARGET_WINDOWS) -#include "input/windows/WINJoystick.h" -#elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) -#include "input/SDLJoystick.h" +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) +#include "input/JoystickManager.h" #endif #if defined(TARGET_ANDROID) @@ -366,6 +364,7 @@ using namespace ANNOUNCEMENT; using namespace PVR; using namespace EPG; using namespace PERIPHERALS; +using namespace JOYSTICK; using namespace XbmcThreads; @@ -1515,9 +1514,9 @@ bool CApplication::Initialize() // reset our screensaver (starts timers etc.) ResetScreenSaver(); -#ifdef HAS_SDL_JOYSTICK - g_Joystick.SetEnabled(CSettings::Get().GetBool("input.enablejoystick") && - CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0 ); +#ifdef HAS_JOYSTICK + CJoystickManager::Get().SetEnabled(CSettings::Get().GetBool("input.enablejoystick") && + CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0); #endif return true; @@ -2970,94 +2969,11 @@ void CApplication::FrameMove(bool processEvents, bool processGUI) bool CApplication::ProcessGamepad(float frameTime) { -#ifdef HAS_SDL_JOYSTICK - if (!m_AppFocused) - return false; - - int iWin = GetActiveWindowID(); - int bid = 0; - g_Joystick.Update(); - if (g_Joystick.GetButton(bid)) - { - // reset Idle Timer - m_idleTimer.StartZero(); - - ResetScreenSaver(); - if (WakeUpScreenSaverAndDPMS()) - { - g_Joystick.Reset(true); - return true; - } - - int actionID; - CStdString actionName; - bool fullrange; - if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, g_Joystick.GetJoystick().c_str(), bid, JACTIVE_BUTTON, actionID, actionName, fullrange)) - { - CAction action(actionID, 1.0f, 0.0f, actionName); - g_Joystick.Reset(); - g_Mouse.SetActive(false); - return ExecuteInputAction(action); - } - else - { - g_Joystick.Reset(); - } - } - if (g_Joystick.GetAxis(bid)) +#ifdef HAS_JOYSTICK + if (m_AppFocused && CJoystickManager::Get().Count() > 0) { - if (g_Joystick.GetAmount() < 0) - { - bid = -bid; - } - - int actionID; - CStdString actionName; - bool fullrange; - if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, g_Joystick.GetJoystick().c_str(), bid, JACTIVE_AXIS, actionID, actionName, fullrange)) - { - ResetScreenSaver(); - if (WakeUpScreenSaverAndDPMS()) - { - return true; - } - - CAction action(actionID, fullrange ? (g_Joystick.GetAmount() + 1.0f)/2.0f : fabs(g_Joystick.GetAmount()), 0.0f, actionName); - g_Joystick.Reset(); - g_Mouse.SetActive(false); - return ExecuteInputAction(action); - } - else - { - g_Joystick.ResetAxis(abs(bid)); - } - } - int position = 0; - if (g_Joystick.GetHat(bid, position)) - { - // reset Idle Timer - m_idleTimer.StartZero(); - - ResetScreenSaver(); - if (WakeUpScreenSaverAndDPMS()) - { - g_Joystick.Reset(); - return true; - } - - int actionID; - CStdString actionName; - bool fullrange; - - bid = position<<16|bid; - - if (bid && CButtonTranslator::GetInstance().TranslateJoystickString(iWin, g_Joystick.GetJoystick().c_str(), bid, JACTIVE_HAT, actionID, actionName, fullrange)) - { - CAction action(actionID, 1.0f, 0.0f, actionName); - g_Joystick.Reset(); - g_Mouse.SetActive(false); - return ExecuteInputAction(action); - } + CJoystickManager::Get().Update(); + return true; } #endif return false; @@ -3263,8 +3179,9 @@ bool CApplication::ProcessJoystickEvent(const std::string& joystickName, int wKe if (WakeUpScreenSaverAndDPMS()) return true; -#ifdef HAS_SDL_JOYSTICK - g_Joystick.Reset(); +#ifdef HAS_JOYSTICK + // Reset the action repeat event + CJoystickManager::Get().Reset(); #endif g_Mouse.SetActive(false); diff --git a/xbmc/Application.h b/xbmc/Application.h index e3a26c5e7cb43..cb0ea6db016d1 100644 --- a/xbmc/Application.h +++ b/xbmc/Application.h @@ -251,6 +251,9 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs bool ExecuteXBMCAction(std::string action); + bool ExecuteInputAction(const CAction &action); + int GetActiveWindowID(void); + static bool OnEvent(XBMC_Event& newEvent); CNetwork& getNetwork(); @@ -448,8 +451,6 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs bool ProcessEventServer(float frameTime); bool ProcessPeripherals(float frameTime); bool ProcessJoystickEvent(const std::string& joystickName, int button, short inputType, float fAmount, unsigned int holdTime = 0); - bool ExecuteInputAction(const CAction &action); - int GetActiveWindowID(void); float NavigationIdleTime(); static bool AlwaysProcess(const CAction& action); diff --git a/xbmc/SystemGlobals.cpp b/xbmc/SystemGlobals.cpp index 2dd1734f7acef..cece7c63d0e77 100644 --- a/xbmc/SystemGlobals.cpp +++ b/xbmc/SystemGlobals.cpp @@ -37,11 +37,6 @@ #ifdef HAS_PYTHON #include "interfaces/python/XBPython.h" #endif -#if defined(TARGET_WINDOWS) -#include "input/windows/WINJoystick.h" -#elif defined(HAS_SDL_JOYSTICK) -#include "input/SDLJoystick.h" -#endif #if defined(HAS_FILESYSTEM_RAR) #include "filesystem/RarManager.h" @@ -63,9 +58,6 @@ CGUITextureManager g_TextureManager; CGUILargeTextureManager g_largeTextureManager; CMouseStat g_Mouse; -#if defined(HAS_SDL_JOYSTICK) - CJoystick g_Joystick; -#endif CGUIPassword g_passwordManager; CGUIInfoManager g_infoManager; diff --git a/xbmc/input/ButtonTranslator.cpp b/xbmc/input/ButtonTranslator.cpp index 4407d8d1fca87..9915d197912d6 100644 --- a/xbmc/input/ButtonTranslator.cpp +++ b/xbmc/input/ButtonTranslator.cpp @@ -35,10 +35,8 @@ #include "utils/XBMCTinyXML.h" #include "XBIRRemote.h" -#if defined(TARGET_WINDOWS) -#include "input/windows/WINJoystick.h" -#elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) -#include "SDLJoystick.h" +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) +#include "JoystickManager.h" #endif #define JOYSTICK_DEFAULT_MAP "_xbmc_" @@ -269,6 +267,28 @@ static const ActionMapping actions[] = { "noop" , ACTION_NOOP} }; +/* static */ +bool CButtonTranslator::IsAnalog(int actionID) +{ + switch (actionID) + { + case ACTION_ANALOG_SEEK_FORWARD: + case ACTION_ANALOG_SEEK_BACK: + case ACTION_ANALOG_FORWARD: + case ACTION_ANALOG_REWIND: + case ACTION_ANALOG_MOVE: + case ACTION_CURSOR_LEFT: + case ACTION_CURSOR_RIGHT: + case ACTION_VOLUME_UP: + case ACTION_VOLUME_DOWN: + case ACTION_ZOOM_IN: + case ACTION_ZOOM_OUT: + return true; + default: + return false; + } +} + static const ActionMapping windows[] = {{"home" , WINDOW_HOME}, {"programs" , WINDOW_PROGRAMS}, @@ -742,7 +762,7 @@ int CButtonTranslator::TranslateLircRemoteString(const char* szDevice, const cha } #endif -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) void CButtonTranslator::MapJoystickActions(int windowID, TiXmlNode *pJoystick) { string joyname = JOYSTICK_DEFAULT_MAP; // default global map name @@ -1126,7 +1146,7 @@ void CButtonTranslator::MapWindowActions(TiXmlNode *pWindow, int windowID) } } -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) if ((pDevice = pWindow->FirstChild("joystick")) != NULL) { // map joystick actions @@ -1461,7 +1481,7 @@ void CButtonTranslator::Clear() lircRemotesMap.clear(); #endif -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) m_joystickButtonMap.clear(); m_joystickAxisMap.clear(); m_joystickHatMap.clear(); diff --git a/xbmc/input/ButtonTranslator.h b/xbmc/input/ButtonTranslator.h index 11ac033cc1e2d..c49cda449efb1 100644 --- a/xbmc/input/ButtonTranslator.h +++ b/xbmc/input/ButtonTranslator.h @@ -25,7 +25,7 @@ #include #include -#include "system.h" // for HAS_EVENT_SERVER, HAS_SDL_JOYSTICK, HAS_LIRC +#include "system.h" // for HAS_EVENT_SERVER, HAS_JOYSTICK, HAS_LIRC #ifdef HAS_EVENT_SERVER #include "network/EventClient.h" @@ -75,6 +75,8 @@ class CButtonTranslator CAction GetAction(int window, const CKey &key, bool fallback = true); + static bool IsAnalog(int actionID); + /*! \brief Translate between a window name and it's id \param window name of the window \return id of the window, or WINDOW_INVALID if not found @@ -92,7 +94,7 @@ class CButtonTranslator #if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) int TranslateLircRemoteString(const char* szDevice, const char *szButton); #endif -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) bool TranslateJoystickString(int window, const char* szDevice, int id, short inputType, int& action, CStdString& strAction, bool &fullrange); @@ -110,7 +112,7 @@ class CButtonTranslator int GetActionCode(int window, int action); int GetActionCode(int window, const CKey &key, CStdString &strAction) const; -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) typedef std::map > JoystickMap; // > int GetActionCode(int window, int id, const JoystickMap &wmap, CStdString &strAction, bool &fullrange) const; #endif @@ -141,7 +143,7 @@ class CButtonTranslator std::map lircRemotesMap; #endif -#if defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#if defined(HAS_JOYSTICK) || defined(HAS_EVENT_SERVER) void MapJoystickActions(int windowID, TiXmlNode *pJoystick); std::map m_joystickButtonMap; // diff --git a/xbmc/input/IJoystick.h b/xbmc/input/IJoystick.h new file mode 100644 index 0000000000000..4372f9f9eca23 --- /dev/null +++ b/xbmc/input/IJoystick.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once + +#include "Joystick.h" + +#include +#include + +/** + * Interface IJoystick + * + * Joysticks are abstracted as devices that repeatedly refresh and report their + * ineternal state. Update() is called by CJoystickManager once per FrameMove() + * to poll for input and should sync the SJoystick struct returned by GetState() + * to the joystick's current state. + */ +class IJoystick +{ +public: + /** + * Implementers should provide the following factories to create IJoystick objects + * (See CJoystickManager::Initialize()): + * + * static void Initialize(JoystickArray &joysticks); + * static void DeInitialize(JoystickArray &joysticks); + */ + + virtual ~IJoystick() { } + + virtual void Update() = 0; + + virtual const JOYSTICK::Joystick &GetState() const = 0; +}; + +typedef std::vector > JoystickArray; diff --git a/xbmc/input/Joystick.cpp b/xbmc/input/Joystick.cpp new file mode 100644 index 0000000000000..03ca665655fcf --- /dev/null +++ b/xbmc/input/Joystick.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "Joystick.h" +#include "settings/AdvancedSettings.h" + +#include + +using namespace JOYSTICK; + +void Hat::Center() +{ + up = right = down = left = false; +} + +bool Hat::operator==(const Hat &rhs) const +{ + return up == rhs.up && right == rhs.right && down == rhs.down && left == rhs.left; +} + +bool &Hat::operator[](unsigned int i) +{ + switch (i) + { + case 0: return up; + case 1: return right; + case 2: return down; + case 3: + default: return left; + } +} + +#define HAT_MAKE_DIRECTION(n, e, s, w) ((n ? 1 : 0) << 3 | (e ? 1 : 0) << 2 | (s ? 1 : 0) << 1 | (w ? 1 : 0)) +const char *Hat::GetDirection() const +{ + switch (HAT_MAKE_DIRECTION(up, right, down, left)) + { + case HAT_MAKE_DIRECTION(1, 0, 0, 0): return "N"; + case HAT_MAKE_DIRECTION(1, 1, 0, 0): return "NE"; + case HAT_MAKE_DIRECTION(0, 1, 0, 0): return "E"; + case HAT_MAKE_DIRECTION(0, 1, 1, 0): return "SE"; + case HAT_MAKE_DIRECTION(0, 0, 1, 0): return "S"; + case HAT_MAKE_DIRECTION(0, 0, 1, 1): return "SW"; + case HAT_MAKE_DIRECTION(0, 0, 0, 1): return "W"; + case HAT_MAKE_DIRECTION(1, 0, 0, 1): return "NW"; + default: return "centered"; + } +} + +void Joystick::ResetState(unsigned int buttonCount /* = GAMEPAD_BUTTON_COUNT */, + unsigned int hatCount /* = GAMEPAD_HAT_COUNT */, + unsigned int axisCount /* = GAMEPAD_AXIS_COUNT */) +{ + buttons.clear(); + hats.clear(); + axes.clear(); + + buttons.resize(buttonCount); + hats.resize(hatCount); + axes.resize(axisCount); +} + +void Joystick::SetAxis(unsigned int axis, long value, long maxAxisAmount) +{ + if (axis >= axes.size()) + return; + if (value > maxAxisAmount) + value = maxAxisAmount; + else if (value < -maxAxisAmount) + value = -maxAxisAmount; + + long deadzoneRange = (long)(g_advancedSettings.m_controllerDeadzone * maxAxisAmount); + + if (value > deadzoneRange) + axes[axis] = (float)(value - deadzoneRange) / (float)(maxAxisAmount - deadzoneRange); + else if (value < -deadzoneRange) + axes[axis] = (float)(value + deadzoneRange) / (float)(maxAxisAmount - deadzoneRange); + else + axes[axis] = 0.0f; +} diff --git a/xbmc/input/Joystick.h b/xbmc/input/Joystick.h new file mode 100644 index 0000000000000..22423ae632f01 --- /dev/null +++ b/xbmc/input/Joystick.h @@ -0,0 +1,105 @@ +#pragma once +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include +#include + +#define JACTIVE_NONE 0x00000000 +#define JACTIVE_BUTTON 0x00000001 +#define JACTIVE_HAT 0x00000002 +#define JACTIVE_AXIS 0x00000004 +#define JACTIVE_HAT_UP 0x01 +#define JACTIVE_HAT_RIGHT 0x02 +#define JACTIVE_HAT_DOWN 0x04 +#define JACTIVE_HAT_LEFT 0x08 + +#define GAMEPAD_BUTTON_COUNT 32 +#define GAMEPAD_HAT_COUNT 4 +#define GAMEPAD_AXIS_COUNT 6 + +#define GAMEPAD_MAX_CONTROLLERS 4 + +namespace JOYSTICK +{ + +/** + * An arrow-based device on a gamepad. Legally, no more than two buttons can be + * pressed, and only if they are adjacent. If no buttons are pressed (or the + * hat is in an invalid state), the hat is considered centered. + */ +struct Hat +{ + Hat() { Center(); } + void Center(); + + bool operator==(const Hat &rhs) const; + bool operator!=(const Hat &rhs) const { return !(*this == rhs); } + + /** + * Iterate through cardinal directions in an ordinal fashion. + * Hat[0] == up + * Hat[1] == right + * Hat[2] == down + * Hat[3] == left + */ + bool &operator[](unsigned int i); + const bool &operator[](unsigned int i) const { return const_cast(*this)[i]; } + + /** + * Helper function to translate this hat into a cardinal direction + * ("N", "NE", "E", ...) or "CENTERED". + */ + const char *GetDirection() const; + + bool up; + bool right; + bool down; + bool left; +}; + +/** + * Abstract representation of a joystick. Joysticks can have buttons, hats and + * analog axes in the range [-1, 1]. Some joystick APIs (the Linux Joystick API, + * for example) report hats as axes with an integer value of -1, 0 or 1. No + * effort should be made to decode these axes back to hats, as this processing + * is done in CJoystickManager. + */ +struct Joystick +{ +public: + Joystick() : id(0) { ResetState(); } + void ResetState(unsigned int buttonCount = GAMEPAD_BUTTON_COUNT, + unsigned int hatCount = GAMEPAD_HAT_COUNT, + unsigned int axisCount = GAMEPAD_AXIS_COUNT); + + /** + * Helper function to normalize a value to maxAxisAmount. + */ + void SetAxis(unsigned int axis, long value, long maxAxisAmount); + + std::string name; + unsigned int id; + std::vector buttons; + std::vector hats; + std::vector axes; +}; + +} // namespace INPUT diff --git a/xbmc/input/JoystickManager.cpp b/xbmc/input/JoystickManager.cpp new file mode 100644 index 0000000000000..53da8a7fc0b5a --- /dev/null +++ b/xbmc/input/JoystickManager.cpp @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "system.h" // for HAS_JOYSTICK +#if defined(HAS_JOYSTICK) + +#include "JoystickManager.h" +#include "Application.h" +#include "ButtonTranslator.h" +#include "guilib/Key.h" +#include "MouseStat.h" +#include "peripherals/devices/PeripheralImon.h" +#include "settings/lib/Setting.h" +#include "utils/log.h" +#include "utils/StdString.h" + +// Include joystick APIs +#if defined(TARGET_WINDOWS) +#include "input/windows/WINJoystickXInput.h" +#include "input/windows/WINJoystickDX.h" +#endif + +#if defined(HAS_LINUX_JOYSTICK) +#include "input/linux/LinuxJoystick.h" +#elif defined(HAS_SDL_JOYSTICK) +#include "input/linux/LinuxJoystickSDL.h" +#endif + +#ifndef ARRAY_LENGTH +#define ARRAY_LENGTH(x) (sizeof((x)) / sizeof((x)[0])) +#endif +#ifndef ABS +#define ABS(X) ((X) >= 0 ? (X) : (-(X))) +#endif + +#define ACTION_FIRST_DELAY 500 // ms +#define ACTION_REPEAT_DELAY 100 // ms +#define AXIS_DIGITAL_DEADZONE 0.5f // Axis must be pushed past this for digital action repeats + +using namespace JOYSTICK; +using namespace PERIPHERALS; +using namespace std; + +void ActionTracker::Reset() +{ + actionID = 0; + name.clear(); + timeout.SetInfinite(); +} + +void ActionTracker::Track(const CAction &action) +{ + if (actionID != action.GetID()) + { + // A new button was pressed, send the action and start tracking it + actionID = action.GetID(); + name = action.GetName(); + timeout.Set(ACTION_FIRST_DELAY); + } + else if (timeout.IsTimePast()) + { + // Same button was pressed, send the action if the repeat delay has elapsed + timeout.Set(ACTION_REPEAT_DELAY); + } +} + +CJoystickManager &CJoystickManager::Get() +{ + static CJoystickManager joystickManager; + return joystickManager; +} + +void CJoystickManager::Initialize() +{ + if (!IsEnabled()) + return; + + // Initialize joystick APIs +#if defined(TARGET_WINDOWS) + CJoystickXInput::Initialize(m_joysticks); + CJoystickDX::Initialize(m_joysticks); +#endif + +#if defined(HAS_LINUX_JOYSTICK) + CLinuxJoystick::Initialize(m_joysticks); +#elif defined(HAS_SDL_JOYSTICK) + CLinuxJoystickSDL::Initialize(m_joysticks); +#endif + + // Truncate array + while (m_joysticks.size() > ARRAY_LENGTH(m_states)) + m_joysticks.pop_back(); + + for (unsigned int i = 0; i < ARRAY_LENGTH(m_states); i++) + m_states[i].ResetState(); +} + +void CJoystickManager::DeInitialize() +{ + // De-initialize joystick APIs +#if defined(TARGET_WINDOWS) + CJoystickXInput::DeInitialize(m_joysticks); + CJoystickDX::DeInitialize(m_joysticks); +#endif + +#if defined(HAS_LINUX_JOYSTICK) + CLinuxJoystick::DeInitialize(m_joysticks); +#elif defined(HAS_SDL_JOYSTICK) + CLinuxJoystickSDL::DeInitialize(m_joysticks); +#endif + + for (unsigned int i = 0; i < ARRAY_LENGTH(m_states); i++) + m_states[i].ResetState(); + + m_actionTracker.Reset(); +} + +void CJoystickManager::Update() +{ + if (!IsEnabled()) + return; + + for (JoystickArray::iterator it = m_joysticks.begin(); it != m_joysticks.end(); it++) + (*it)->Update(); + + ProcessStateChanges(); +} + +void CJoystickManager::ProcessStateChanges() +{ + for (unsigned int joyID = 0; joyID < m_joysticks.size(); joyID++) + { + ProcessButtonPresses(m_states[joyID], m_joysticks[joyID]->GetState(), joyID); + ProcessHatPresses(m_states[joyID], m_joysticks[joyID]->GetState(), joyID); + ProcessAxisMotion(m_states[joyID], m_joysticks[joyID]->GetState(), joyID); + } + + // If tracking an action and the time has elapsed, execute the action now + if (m_actionTracker.actionID && m_actionTracker.timeout.IsTimePast()) + { + CAction action(m_actionTracker.actionID, 1.0f, 0.0f, m_actionTracker.name); + g_application.ExecuteInputAction(action); + m_actionTracker.Track(action); // Update the timer + } + + // Reset the wakeup check, so that the check will be performed for the next button press also + ResetWakeup(); +} + +void CJoystickManager::ProcessButtonPresses(Joystick &oldState, const Joystick &newState, unsigned int joyID) +{ + for (unsigned int i = 0; i < newState.buttons.size(); i++) + { + if (oldState.buttons[i] == newState.buttons[i]) + continue; + oldState.buttons[i] = newState.buttons[i]; + + CLog::Log(LOGDEBUG, "Joystick %d button %d %s", joyID, i + 1, newState.buttons[i] ? "pressed" : "unpressed"); + + // Check to see if an action is registered for the button first + int actionID; + CStdString actionName; + bool fullrange; + // Button ID is i + 1 + if (!CButtonTranslator::GetInstance().TranslateJoystickString(g_application.GetActiveWindowID(), + newState.name.c_str(), i + 1, JACTIVE_BUTTON, actionID, actionName, fullrange)) + { + CLog::Log(LOGDEBUG, "-> Joystick %d button %d no registered action", joyID, i + 1); + continue; + } + g_Mouse.SetActive(false); + + // Ignore all button presses during this ProcessStateChanges() if we woke + // up the screensaver (but always send joypad unpresses) + if (!Wakeup() && newState.buttons[i]) + { + CAction action(actionID, 1.0f, 0.0f, actionName); + g_application.ExecuteInputAction(action); + // Track the button press for deferred repeated execution + m_actionTracker.Track(action); + } + else if (!newState.buttons[i]) + { + m_actionTracker.Reset(); // If a button was released, reset the tracker + } + } +} + +void CJoystickManager::ProcessHatPresses(Joystick &oldState, const Joystick &newState, unsigned int joyID) +{ + for (unsigned int i = 0; i < newState.hats.size(); i++) + { + Hat &oldHat = oldState.hats[i]; + const Hat &newHat = newState.hats[i]; + if (oldHat == newHat) + continue; + + CLog::Log(LOGDEBUG, "Joystick %d hat %d new direction: %s", joyID, i + 1, newHat.GetDirection()); + + // Up, right, down, left + for (unsigned int j = 0; j < 4; j++) + { + if (oldHat[j] == newHat[j]) + continue; + oldHat[j] = newHat[j]; + + int actionID; + CStdString actionName; + bool fullrange; + // Up is (1 << 0), right (1 << 1), down (1 << 2), left (1 << 3). Hat ID is i + 1 + int buttonID = (1 << j) << 16 | (i + 1); + if (!buttonID || !CButtonTranslator::GetInstance().TranslateJoystickString(g_application.GetActiveWindowID(), + newState.name.c_str(), buttonID, JACTIVE_HAT, actionID, actionName, fullrange)) + { + static const char *dir[] = {"UP", "RIGHT", "DOWN", "LEFT"}; + CLog::Log(LOGDEBUG, "-> Joystick %d hat %d direction %s no registered action", joyID, i + 1, dir[j]); + continue; + } + g_Mouse.SetActive(false); + + // Ignore all button presses during this ProcessStateChanges() if we woke + // up the screensaver (but always send joypad unpresses) + if (!Wakeup() && newHat[j]) + { + CAction action(actionID, 1.0f, 0.0f, actionName); + g_application.ExecuteInputAction(action); + // Track the hat press for deferred repeated execution + m_actionTracker.Track(action); + } + else if (!newHat[j]) + { + // If a hat was released, reset the tracker + m_actionTracker.Reset(); + } + } + } +} + +void CJoystickManager::ProcessAxisMotion(Joystick &oldState, const Joystick &newState, unsigned int joyID) +{ + for (unsigned int i = 0; i < newState.axes.size(); i++) + { + // Absolute magnitude + float absAxis = ABS(newState.axes[i]); + + // Only send one "centered" message + if (absAxis < 0.01f) + { + if (ABS(oldState.axes[i]) < 0.01f) + { + // The values might not have been exactly equal, so make them + oldState.axes[i] = newState.axes[i]; + continue; + } + CLog::Log(LOGDEBUG, "Joystick %d axis %d centered", joyID, i + 1); + } + // Note: don't overwrite oldState until we know whether the action is analog or digital + + int actionID; + CStdString actionName; + bool fullrange; + // Axis ID is i + 1, and negative if newState.axes[i] < 0 + if (!CButtonTranslator::GetInstance().TranslateJoystickString(g_application.GetActiveWindowID(), + newState.name.c_str(), newState.axes[i] >= 0.0f ? (i + 1) : -(int)(i + 1), JACTIVE_AXIS, actionID, + actionName, fullrange)) + { + continue; + } + g_Mouse.SetActive(false); + + // Use newState.axes[i] as the second about so subscribers can recover the original value + CAction action(actionID, fullrange ? (newState.axes[i] + 1.0f) / 2.0f : absAxis, newState.axes[i], actionName); + + // For digital event, we treat action repeats like buttons and hats + if (!CButtonTranslator::IsAnalog(actionID)) + { + // NOW we overwrite old action and continue if no change in digital states + bool bContinue = !((ABS(oldState.axes[i]) >= AXIS_DIGITAL_DEADZONE) ^ (absAxis >= AXIS_DIGITAL_DEADZONE)); + oldState.axes[i] = newState.axes[i]; + if (bContinue) + continue; + + if (absAxis >= 0.01f) // Because we already sent a "centered" message + CLog::Log(LOGDEBUG, "Joystick %d axis %d %s", joyID, i + 1, + absAxis >= AXIS_DIGITAL_DEADZONE ? "activated" : "deactivated (but not centered)"); + + if (!Wakeup() && absAxis >= AXIS_DIGITAL_DEADZONE) + { + g_application.ExecuteInputAction(action); + m_actionTracker.Track(action); + } + else if (absAxis < AXIS_DIGITAL_DEADZONE) + { + m_actionTracker.Reset(); + } + } + else // CButtonTranslator::IsAnalog(actionID) + { + // We don't log about analog actions because they are sent every frame + oldState.axes[i] = newState.axes[i]; + + if (Wakeup()) + continue; + + if (newState.axes[i] != 0.0f) + g_application.ExecuteInputAction(action); + + // The presence of analog actions disables others from being tracked + m_actionTracker.Reset(); + } + } +} + +bool CJoystickManager::Wakeup() +{ + static bool bWokenUp = false; + + // Refresh bWokenUp after every call to ResetWakeup() (which sets m_bWakeupChecked to false) + if (!m_bWakeupChecked) + { + m_bWakeupChecked = true; + + // Reset the timers and check to see if we have woken the application + g_application.ResetSystemIdleTimer(); + g_application.ResetScreenSaver(); + bWokenUp = g_application.WakeUpScreenSaverAndDPMS(); + } + return bWokenUp; +} + +void CJoystickManager::SetEnabled(bool enabled /* = true */) +{ + if (enabled && !m_bEnabled) + { + m_bEnabled = true; + Initialize(); + } + else if (!enabled && m_bEnabled) + { + DeInitialize(); + m_bEnabled = false; + } +} + +void CJoystickManager::OnSettingChanged(const CSetting *setting) +{ + if (setting == NULL) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == "input.enablejoystick") + { + SetEnabled(((CSettingBool*)setting)->GetValue() && CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0); + } +} + +#endif // defined(HAS_JOYSTICK) diff --git a/xbmc/input/JoystickManager.h b/xbmc/input/JoystickManager.h new file mode 100644 index 0000000000000..eeeb2832b381a --- /dev/null +++ b/xbmc/input/JoystickManager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once + +#include "IJoystick.h" +#include "settings/lib/ISettingCallback.h" +#include "threads/SystemClock.h" + +#include + +class CAction; + +namespace JOYSTICK +{ + +/** + * Track key presses for deferred action repeats. + */ +struct ActionTracker +{ + ActionTracker() { Reset(); } + void Reset(); + void Track(const CAction &action); + + int actionID; // Action ID, or 0 if not tracking any action + std::string name; // Action name + XbmcThreads::EndTime timeout; // Timeout until action is repeated +}; + +/** + * Class to manage all connected joysticks. + */ +class CJoystickManager : public ISettingCallback +{ +private: + CJoystickManager() : m_bEnabled(false), m_bWakeupChecked(false) { } + virtual ~CJoystickManager() { DeInitialize(); } + +public: + static CJoystickManager &Get(); + + void SetEnabled(bool enabled = true); + bool IsEnabled() const { return m_bEnabled; } + void Update(); + unsigned int Count() const { return m_joysticks.size(); } + void Reinitialize() { Initialize(); } + void Reset() { m_actionTracker.Reset(); } + + // Inherited from ISettingCallback + virtual void OnSettingChanged(const CSetting *setting); + +private: + void Initialize(); + void DeInitialize(); + + /** + * After updating, look for changes in state. + * @param oldState - previous joystick state, set to newState as a post-condition + * @param newState - the updated joystick state + * @param joyID - the ID of the joystick being processed + */ + void ProcessStateChanges(); + void ProcessButtonPresses(Joystick &oldState, const Joystick &newState, unsigned int joyID); + void ProcessHatPresses(Joystick &oldState, const Joystick &newState, unsigned int joyID); + void ProcessAxisMotion(Joystick &oldState, const Joystick &newState, unsigned int joyID); + + // Returns true if this wakes up from the screensaver + bool Wakeup(); + // Allows Wakeup() to perform another wakeup check + void ResetWakeup() { m_bWakeupChecked = false; } + + JoystickArray m_joysticks; + Joystick m_states[GAMEPAD_MAX_CONTROLLERS]; + bool m_bEnabled; + bool m_bWakeupChecked; // true if WakeupCheck() has been called + + ActionTracker m_actionTracker; +}; + +} // namespace INPUT diff --git a/xbmc/input/Makefile b/xbmc/input/Makefile index f74c2b3c65977..d3e23903e00dc 100644 --- a/xbmc/input/Makefile +++ b/xbmc/input/Makefile @@ -1,9 +1,10 @@ SRCS=ButtonTranslator.cpp \ InertialScrollingHandler.cpp \ + Joystick.cpp \ + JoystickManager.cpp \ KeyboardLayoutConfiguration.cpp \ KeyboardStat.cpp \ MouseStat.cpp \ - SDLJoystick.cpp \ XBMC_keytable.cpp \ LIB=input.a diff --git a/xbmc/input/SDLJoystick.h b/xbmc/input/SDLJoystick.h deleted file mode 100644 index b5fa48a5207b5..0000000000000 --- a/xbmc/input/SDLJoystick.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2007-2013 Team XBMC - * http://xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#ifndef SDL_JOYSTICK_H -#define SDL_JOYSTICK_H - -#include "system.h" // for HAS_SDL_JOYSTICK -#include "settings/lib/ISettingCallback.h" -#include -#include - -#define JACTIVE_BUTTON 0x00000001 -#define JACTIVE_AXIS 0x00000002 -#define JACTIVE_HAT 0x00000004 -#define JACTIVE_NONE 0x00000000 -#define JACTIVE_HAT_UP 0x01 -#define JACTIVE_HAT_RIGHT 0x02 -#define JACTIVE_HAT_DOWN 0x04 -#define JACTIVE_HAT_LEFT 0x08 - -#ifdef HAS_SDL_JOYSTICK - -#include -#include - -#define MAX_AXES 64 -#define MAX_AXISAMOUNT 32768 - - -// Class to manage all connected joysticks - -class CJoystick : public ISettingCallback -{ -public: - CJoystick(); - - virtual void OnSettingChanged(const CSetting *setting); - - void Initialize(); - void Reset(bool axis=false); - void ResetAxis(int axisId) { m_Amount[axisId] = 0; } - void Update(); - void Update(SDL_Event& event); - bool GetButton (int& id, bool consider_repeat=true); - bool GetAxis (int &id); - bool GetHat (int &id, int &position, bool consider_repeat=true); - std::string GetJoystick() { return (m_JoyId>-1)?m_JoystickNames[m_JoyId]:""; } - int GetAxisWithMaxAmount(); - float GetAmount(int axis); - float GetAmount() { return GetAmount(m_AxisId); } - bool IsEnabled() const { return m_joystickEnabled; } - void SetEnabled(bool enabled = true); - float SetDeadzone(float val); - bool Reinitialize(); - -private: - void SetAxisActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_AXIS):(m_ActiveFlags&(~JACTIVE_AXIS)); } - void SetButtonActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_BUTTON):(m_ActiveFlags&(~JACTIVE_BUTTON)); } - void SetHatActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_HAT):(m_ActiveFlags&(~JACTIVE_HAT)); } - bool IsButtonActive() { return (m_ActiveFlags & JACTIVE_BUTTON) == JACTIVE_BUTTON; } - bool IsAxisActive() { return (m_ActiveFlags & JACTIVE_AXIS) == JACTIVE_AXIS; } - bool IsHatActive() { return (m_ActiveFlags & JACTIVE_HAT) == JACTIVE_HAT; } - - bool ReleaseJoysticks(); - - int m_Amount[MAX_AXES]; - int m_AxisId; - int m_ButtonId; - uint8_t m_HatState; - int m_HatId; - int m_JoyId; - int m_NumAxes; - int m_DeadzoneRange; - bool m_joystickEnabled; - uint32_t m_pressTicksButton; - uint32_t m_pressTicksHat; - uint8_t m_ActiveFlags; - std::vector m_Joysticks; - std::vector m_JoystickNames; -}; - -extern CJoystick g_Joystick; - -#endif - -#endif diff --git a/xbmc/input/linux/LinuxJoystick.cpp b/xbmc/input/linux/LinuxJoystick.cpp new file mode 100644 index 0000000000000..1d0b0aaa2d03b --- /dev/null +++ b/xbmc/input/linux/LinuxJoystick.cpp @@ -0,0 +1,327 @@ +/* +* Copyright (C) 2007-2013 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, see +* . +* +* Parts of this file are Copyright (C) 2009 Stephen Kitt +*/ + +#include "LinuxJoystick.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The following values come from include/input.h in the kernel source; the + * small variant is used up to version 2.6.27, the large one from 2.6.28 + * onwards. We need to handle both values because the kernel doesn't; it only + * expects one of the values, and we need to determine which one at run-time. + */ +#define KEY_MAX_LARGE 0x2FF +#define KEY_MAX_SMALL 0x1FF + +/* Axis map size. */ +#define AXMAP_SIZE (ABS_MAX + 1) + +/* Button map size. */ +#define BTNMAP_SIZE (KEY_MAX_LARGE - BTN_MISC + 1) + +/* The following values come from include/joystick.h in the kernel source. */ +#define JSIOCSBTNMAP_LARGE _IOW('j', 0x33, __u16[KEY_MAX_LARGE - BTN_MISC + 1]) +#define JSIOCSBTNMAP_SMALL _IOW('j', 0x33, __u16[KEY_MAX_SMALL - BTN_MISC + 1]) +#define JSIOCGBTNMAP_LARGE _IOR('j', 0x34, __u16[KEY_MAX_LARGE - BTN_MISC + 1]) +#define JSIOCGBTNMAP_SMALL _IOR('j', 0x34, __u16[KEY_MAX_SMALL - BTN_MISC + 1]) + +#define JOYSTICK_UNKNOWN "Unknown XBMC-Compatible Linux Joystick" +#define MAX_AXIS 32767 + +using namespace JOYSTICK; +using namespace std; + +static const char *axis_names[ABS_MAX + 1] = +{ + "X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder", + "Wheel", "Gas", "Brake", "?", "?", "?", "?", "?", + "Hat0X", "Hat0Y", "Hat1X", "Hat1Y", "Hat2X", "Hat2Y", "Hat3X", "Hat3Y", + "?", "?", "?", "?", "?", "?", "?", +}; + +static const char *button_names[KEY_MAX - BTN_MISC + 1] = +{ + "Btn0", "Btn1", "Btn2", "Btn3", "Btn4", "Btn5", "Btn6", + "Btn7", "Btn8", "Btn9", "?", "?", "?", "?", + "?", "?", "LeftBtn", "RightBtn", "MiddleBtn", "SideBtn", "ExtraBtn", + "ForwardBtn", "BackBtn", "TaskBtn", "?", "?", "?", "?", + "?", "?", "?", "?", "Trigger", "ThumbBtn", "ThumbBtn2", + "TopBtn", "TopBtn2", "PinkieBtn", "BaseBtn", "BaseBtn2", "BaseBtn3", "BaseBtn4", + "BaseBtn5", "BaseBtn6", "BtnDead", "BtnA", "BtnB", "BtnC", "BtnX", + "BtnY", "BtnZ", "BtnTL", "BtnTR", "BtnTL2", "BtnTR2", "BtnSelect", + "BtnStart", "BtnMode", "BtnThumbL", "BtnThumbR", "?", "?", "?", + "?", "?", "?", "?", "?", "?", "?", + "?", "?", "?", "?", "?", "?", "?", + "WheelBtn", "Gear up", +}; + +CLinuxJoystick::CLinuxJoystick(int fd, unsigned int id, const char *name, const std::string &filename, + unsigned char buttons, unsigned char axes) : m_state(), m_fd(fd), m_filename(filename) +{ + m_state.id = id; + m_state.name = name; + m_state.ResetState(buttons, 0, axes); +} + +CLinuxJoystick::~CLinuxJoystick() +{ + close(m_fd); +} + +/* static */ +void CLinuxJoystick::Initialize(JoystickArray &joysticks) +{ + // TODO: Use udev to grab device names instead of reading /dev/js* + string inputDir("/dev/input"); + DIR *pd = opendir(inputDir.c_str()); + if (pd == NULL) + { + CLog::Log(LOGERROR, "%s: can't open /dev/input (errno=%d)", __FUNCTION__, errno); + return; + } + + dirent *pDirent; + while ((pDirent = readdir(pd)) != NULL) + { + if (strncmp(pDirent->d_name, "js", 2) == 0) + { + // Found a joystick device + string filename(inputDir + "/" + pDirent->d_name); + CLog::Log(LOGNOTICE, "CLinuxJoystick::Initialize: opening joystick %s", filename.c_str()); + + int fd = open(filename.c_str(), O_RDONLY); + if (fd < 0) + { + CLog::Log(LOGERROR, "%s: can't open %s (errno=%d)", __FUNCTION__, filename.c_str(), errno); + continue; + } + + unsigned char axes = 0; + unsigned char buttons = 0; + int version = 0x000000; + char name[128] = JOYSTICK_UNKNOWN; + + if (ioctl(fd, JSIOCGVERSION, &version) < 0 || + ioctl(fd, JSIOCGAXES, &axes) < 0 || + ioctl(fd, JSIOCGBUTTONS, &buttons) < 0 || + ioctl(fd, JSIOCGNAME(128), name) < 0) + { + CLog::Log(LOGERROR, "%s: failed ioctl() (errno=%d)", __FUNCTION__, errno); + close(fd); + continue; + } + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + { + CLog::Log(LOGERROR, "%s: failed fcntl() (errno=%d)", __FUNCTION__, errno); + close(fd); + continue; + } + + // We don't support the old (0.x) interface + if (version < 0x010000) + { + CLog::Log(LOGERROR, "%s: old (0.x) interface is not supported (version=%08x)", __FUNCTION__, version); + close(fd); + continue; + } + + CLog::Log(LOGNOTICE, "%s: Enabled Joystick: \"%s\" (Linux Joystick API)", __FUNCTION__, name); + CLog::Log(LOGNOTICE, "%s: driver version is %d.%d.%d", __FUNCTION__, + version >> 16, (version >> 8) & 0xff, version & 0xff); + + uint16_t buttonMap[BTNMAP_SIZE]; + uint8_t axisMap[AXMAP_SIZE]; + + if (GetButtonMap(fd, buttonMap) < 0 || GetAxisMap(fd, axisMap) < 0) + { + CLog::Log(LOGERROR, "%s: can't get button or axis map", __FUNCTION__); + // I assume this isn't a fatal error... + } + + /* Determine whether the button map is usable. */ + bool buttonMapOK = true; + for (int i = 0; buttonMapOK && i < buttons; i++) + { + if (buttonMap[i] < BTN_MISC || buttonMap[i] > KEY_MAX) + { + buttonMapOK = false; + break; + } + } + + if (!buttonMapOK) + { + /* buttonMap out of range for names. Don't print any. */ + CLog::Log(LOGERROR, "%s: XBMC is not fully compatible with your kernel. Unable to retrieve button map!", + __FUNCTION__); + CLog::Log(LOGNOTICE, "%s: Joystick \"%s\" has %d buttons and %d axes", __FUNCTION__, + name, buttons, axes); + } + else + { + ostringstream strButtons; + for (int i = 0; i < buttons; i++) + { + strButtons << button_names[buttonMap[i] - BTN_MISC]; + if (i < buttons - 1) + strButtons << ", "; + } + CLog::Log(LOGNOTICE, "Buttons: %s", strButtons.str().c_str()); + + ostringstream strAxes; + for (int i = 0; i < axes; i++) + { + strAxes << axis_names[axisMap[i]]; + if (i < axes - 1) + strAxes << ", "; + } + CLog::Log(LOGNOTICE, "Axes: %s", strAxes.str().c_str()); + } + + // Got enough information, time to move on to the next joystick + joysticks.push_back(boost::shared_ptr(new CLinuxJoystick(fd, joysticks.size(), + name, filename, buttons, axes))); + } + } + closedir(pd); +} + +/** + * Retrieves the current button map in the given array, which must contain at + * least BTNMAP_SIZE elements. Returns the result of the ioctl(): negative in + * case of an error, 0 otherwise for kernels up to 2.6.30, the length of the + * array actually copied for later kernels. + */ +int CLinuxJoystick::GetButtonMap(int fd, uint16_t *buttonMap) +{ + static int joyGetButtonMapIoctl = 0; + int ioctls[] = { JSIOCGBTNMAP, JSIOCGBTNMAP_LARGE, JSIOCGBTNMAP_SMALL, 0 }; + + if (joyGetButtonMapIoctl != 0) + { + /* We already know which ioctl to use. */ + return ioctl(fd, joyGetButtonMapIoctl, buttonMap); + } + else + { + return DetermineIoctl(fd, ioctls, buttonMap, joyGetButtonMapIoctl); + } +} + +/** + * Retrieves the current axis map in the given array, which must contain at + * least AXMAP_SIZE elements. + */ +int CLinuxJoystick::GetAxisMap(int fd, uint8_t *axisMap) +{ + return ioctl(fd, JSIOCGAXMAP, axisMap); +} + +/* static */ +int CLinuxJoystick::DetermineIoctl(int fd, int *ioctls, uint16_t *buttonMap, int &ioctl_used) +{ + int retval = 0; + + /* Try each ioctl in turn. */ + for (int i = 0; ioctls[i] != 0; i++) + { + retval = ioctl(fd, ioctls[i], (void*)buttonMap); + if (retval >= 0) + { + /* The ioctl did something. */ + ioctl_used = ioctls[i]; + return retval; + } + else if (errno != -EINVAL) + { + /* Some other error occurred. */ + return retval; + } + } + return retval; +} + +/* static */ +void CLinuxJoystick::DeInitialize(JoystickArray &joysticks) +{ + for (int i = 0; i < (int)joysticks.size(); i++) + { + if (boost::dynamic_pointer_cast(joysticks[i])) + joysticks.erase(joysticks.begin() + i--); + } +} + +void CLinuxJoystick::Update() +{ + js_event joyEvent; + + while(true) + { + // Flush the driver queue + if (read(m_fd, &joyEvent, sizeof(joyEvent)) != sizeof(joyEvent)) + { + if (errno == EAGAIN) + { + // The circular driver queue holds 64 events. If compiling your own driver, + // you can increment this size bumping up JS_BUFF_SIZE in joystick.h + return; + } + else + { + CLog::Log(LOGERROR, "%s: failed to read joystick \"%s\" on %s", __FUNCTION__, + m_state.name.c_str(), m_filename.c_str()); + return; + } + } + + // The possible values of joystickEvent.type are: + // JS_EVENT_BUTTON 0x01 /* button pressed/released */ + // JS_EVENT_AXIS 0x02 /* joystick moved */ + // JS_EVENT_INIT 0x80 /* (flag) initial state of device */ + + // Ignore initial events, because they mess up the buttons + switch (joyEvent.type) + { + case JS_EVENT_BUTTON: + if (joyEvent.number < m_state.buttons.size()) + m_state.buttons[joyEvent.number] = joyEvent.value; + break; + case JS_EVENT_AXIS: + if (joyEvent.number < m_state.axes.size()) + m_state.SetAxis(joyEvent.number, joyEvent.value, MAX_AXIS); + break; + default: + break; + } + } +} diff --git a/xbmc/input/linux/LinuxJoystick.h b/xbmc/input/linux/LinuxJoystick.h new file mode 100644 index 0000000000000..d8be9c490f8f1 --- /dev/null +++ b/xbmc/input/linux/LinuxJoystick.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + * Parts of this file are Copyright (C) 2009 Stephen Kitt + */ + +#pragma once + +#include "input/IJoystick.h" + +#include +#include + +namespace JOYSTICK +{ + +class CLinuxJoystick : public IJoystick +{ +public: + static void Initialize(JoystickArray &joysticks); + static void DeInitialize(JoystickArray &joysticks); + + virtual ~CLinuxJoystick(); + virtual void Update(); + virtual const JOYSTICK::Joystick &GetState() const { return m_state; } + +private: + CLinuxJoystick(int fd, unsigned int id, const char *name, const std::string &filename, unsigned char buttons, unsigned char axes); + + static int GetButtonMap(int fd, uint16_t *buttonMap); + static int GetAxisMap(int fd, uint8_t *axisMap); + static int DetermineIoctl(int fd, int *ioctls, uint16_t *buttonMap, int &ioctl_used); + + JOYSTICK::Joystick m_state; + int m_fd; + std::string m_filename; // for debugging purposes +}; + +} diff --git a/xbmc/input/linux/LinuxJoystickSDL.cpp b/xbmc/input/linux/LinuxJoystickSDL.cpp new file mode 100644 index 0000000000000..1271b457d126e --- /dev/null +++ b/xbmc/input/linux/LinuxJoystickSDL.cpp @@ -0,0 +1,138 @@ +/* +* Copyright (C) 2007-2013 Team XBMC +* http://www.xbmc.org +* +* This Program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2, or (at your option) +* any later version. +* +* This Program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with XBMC; see the file COPYING. If not, see +* . +* +*/ + +#include "system.h" +#if defined(HAS_SDL_JOYSTICK) + +#include "LinuxJoystickSDL.h" +#include "utils/log.h" +#include "utils/StringUtils.h" + +#include +#include + +#define MAX_AXES 64 +#define MAX_AXISAMOUNT 32768 + +using namespace JOYSTICK; + +CLinuxJoystickSDL::CLinuxJoystickSDL(std::string name, SDL_Joystick *pJoystick, unsigned int id) : m_pJoystick(pJoystick), m_state() +{ + m_state.id = id; + m_state.name = name; + m_state.ResetState(SDL_JoystickNumButtons(m_pJoystick), SDL_JoystickNumButtons(m_pJoystick), SDL_JoystickNumButtons(m_pJoystick));; + + CLog::Log(LOGNOTICE, "Enabled Joystick: \"%s\" (SDL)", name.c_str()); + CLog::Log(LOGNOTICE, "Details: Total Axes: %u Total Hats: %u Total Buttons: %u", + (unsigned int)m_state.axes.size(), (unsigned int)m_state.hats.size(), (unsigned int)m_state.buttons.size()); +} + +/* static */ +void CLinuxJoystickSDL::Initialize(JoystickArray &joysticks) +{ + DeInitialize(joysticks); + + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != 0) + { + CLog::Log(LOGERROR, "(Re)start joystick subsystem failed : %s", SDL_GetError()); + return; + } + + // Any joysticks connected? + if (SDL_NumJoysticks() > 0) + { + // Load joystick names and open all connected joysticks + for (int i = 0 ; i < SDL_NumJoysticks(); i++) + { + SDL_Joystick *joy = SDL_JoystickOpen(i); +#if defined(TARGET_DARWIN) + // On OS X, the 360 controllers are handled externally, since the SDL code is + // really buggy and doesn't handle disconnects. + if (std::string(SDL_JoystickName(i)).find("360") != std::string::npos) + { + CLog::Log(LOGNOTICE, "Ignoring joystick: %s", SDL_JoystickName(i)); + continue; + } +#endif + if (joy) + { + // Some (Microsoft) Keyboards are recognized as Joysticks by modern kernels + // Don't enumerate them + // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/390959 + // NOTICE: Enabled Joystick: Microsoft Wired Keyboard 600 + // Details: Total Axis: 37 Total Hats: 0 Total Buttons: 57 + // NOTICE: Enabled Joystick: Microsoft Microsoft® 2.4GHz Transceiver v6.0 + // Details: Total Axis: 37 Total Hats: 0 Total Buttons: 57 + int num_axis = SDL_JoystickNumAxes(joy); + int num_buttons = SDL_JoystickNumButtons(joy); + if (num_axis > 20 && num_buttons > 50) + CLog::Log(LOGNOTICE, "Your Joystick seems to be a Keyboard, ignoring it: %s Axis: %d Buttons: %d", + SDL_JoystickName(i), num_axis, num_buttons); + else + joysticks.push_back(boost::shared_ptr(new CLinuxJoystickSDL(SDL_JoystickName(i), + joy, joysticks.size()))); + } + } + } + + // Disable joystick events, since we'll be polling them + SDL_JoystickEventState(SDL_DISABLE); +} + +/* static */ +void CLinuxJoystickSDL::DeInitialize(JoystickArray &joysticks) +{ + for (int i = 0; i < (int)joysticks.size(); i++) + { + if (boost::dynamic_pointer_cast(joysticks[i])) + joysticks.erase(joysticks.begin() + i--); + } + // Restart SDL joystick subsystem + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + if (SDL_WasInit(SDL_INIT_JOYSTICK) != 0) + CLog::Log(LOGERROR, "Stopping joystick SDL subsystem failed"); +} + +void CLinuxJoystickSDL::Update() +{ + // Update the state of all opened joysticks + SDL_JoystickUpdate(); + + // Gamepad buttons + for (unsigned int b = 0; b < m_state.buttons.size(); b++) + m_state.buttons[b] = (SDL_JoystickGetButton(m_pJoystick, b) ? true : false); + + // Gamepad hats + for (unsigned int h = 0; h < m_state.hats.size(); h++) + { + m_state.hats[h].Center(); + uint8_t hat = SDL_JoystickGetHat(m_pJoystick, h); + if (hat & SDL_HAT_UP) m_state.hats[h].up = true; + else if (hat & SDL_HAT_DOWN) m_state.hats[h].down = true; + if (hat & SDL_HAT_RIGHT) m_state.hats[h].right = true; + else if (hat & SDL_HAT_LEFT) m_state.hats[h].left = true; + } + + // Gamepad axes + for (unsigned int a = 0; a < m_state.axes.size(); a++) + m_state.SetAxis(a, (long)SDL_JoystickGetAxis(m_pJoystick, a), MAX_AXISAMOUNT); +} + +#endif // HAS_SDL_JOYSTICK diff --git a/xbmc/input/linux/LinuxJoystickSDL.h b/xbmc/input/linux/LinuxJoystickSDL.h new file mode 100644 index 0000000000000..4cc21ec3379c5 --- /dev/null +++ b/xbmc/input/linux/LinuxJoystickSDL.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once + +#include "input/IJoystick.h" + +#include + +struct _SDL_Joystick; +typedef struct _SDL_Joystick SDL_Joystick; + +class CLinuxJoystickSDL : public IJoystick +{ +public: + static void Initialize(JoystickArray &joysticks); + static void DeInitialize(JoystickArray &joysticks); + + virtual ~CLinuxJoystickSDL() { } + virtual void Update(); + virtual const JOYSTICK::Joystick &GetState() const { return m_state; } + +private: + CLinuxJoystickSDL(std::string name, SDL_Joystick *pJoystick, unsigned int id); + + SDL_Joystick *m_pJoystick; + JOYSTICK::Joystick m_state; +}; diff --git a/xbmc/input/linux/Makefile.in b/xbmc/input/linux/Makefile.in index 56b428e3ac65b..f112491f98970 100644 --- a/xbmc/input/linux/Makefile.in +++ b/xbmc/input/linux/Makefile.in @@ -1,5 +1,7 @@ SRCS=LIRC.cpp \ - LinuxInputDevices.cpp + LinuxInputDevices.cpp \ + LinuxJoystick.cpp \ + LinuxJoystickSDL.cpp # xkbcommon detail ifeq (@USE_XKBCOMMON@,1) diff --git a/xbmc/input/windows/WINJoystick.cpp b/xbmc/input/windows/WINJoystick.cpp deleted file mode 100644 index 1677dd7d22977..0000000000000 --- a/xbmc/input/windows/WINJoystick.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* -* Copyright (C) 2012-2013 Team XBMC - * http://xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#include "WINJoystick.h" -#include "input/ButtonTranslator.h" -#include "peripherals/devices/PeripheralImon.h" -#include "settings/AdvancedSettings.h" -#include "settings/lib/Setting.h" -#include "utils/log.h" - -#include - -#include -#include - -using namespace std; - -extern HWND g_hWnd; - -#define MAX_AXISAMOUNT 32768 -#define AXIS_MIN -32768 /* minimum value for axis coordinate */ -#define AXIS_MAX 32767 /* maximum value for axis coordinate */ - -#if !defined(HAS_SDL) -#define SDL_HAT_CENTERED 0x00 -#define SDL_HAT_UP 0x01 -#define SDL_HAT_RIGHT 0x02 -#define SDL_HAT_DOWN 0x04 -#define SDL_HAT_LEFT 0x08 -#define SDL_HAT_RIGHTUP (SDL_HAT_RIGHT|SDL_HAT_UP) -#define SDL_HAT_RIGHTDOWN (SDL_HAT_RIGHT|SDL_HAT_DOWN) -#define SDL_HAT_LEFTUP (SDL_HAT_LEFT|SDL_HAT_UP) -#define SDL_HAT_LEFTDOWN (SDL_HAT_LEFT|SDL_HAT_DOWN) -#endif - -CJoystick::CJoystick() -{ - CSingleLock lock(m_critSection); - Reset(true); - m_joystickEnabled = false; - m_NumAxes = 0; - m_AxisId = 0; - m_JoyId = 0; - m_ButtonId = 0; - m_HatId = 0; - m_HatState = SDL_HAT_CENTERED; - m_ActiveFlags = JACTIVE_NONE; - SetDeadzone(0); - - m_pDI = NULL; - m_lastPressTicks = 0; - m_lastTicks = 0; -} - -CJoystick::~CJoystick() -{ - ReleaseJoysticks(); -} - -void CJoystick::OnSettingChanged(const CSetting *setting) -{ - if (setting == NULL) - return; - - const std::string &settingId = setting->GetId(); - if (settingId == "input.enablejoystick") - SetEnabled(((CSettingBool*)setting)->GetValue() && PERIPHERALS::CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0); -} - -void CJoystick::ReleaseJoysticks() -{ - CSingleLock lock(m_critSection); - // Unacquire the device one last time just in case - // the app tried to exit while the device is still acquired. - for(std::vector::iterator it = m_pJoysticks.begin(); it != m_pJoysticks.end(); ++it) - { - if( (*it) ) - (*it)->Unacquire(); - SAFE_RELEASE( (*it) ); - } - m_pJoysticks.clear(); - m_JoystickNames.clear(); - m_devCaps.clear(); - m_HatId = 0; - m_ButtonId = 0; - m_HatState = SDL_HAT_CENTERED; - m_ActiveFlags = JACTIVE_NONE; - Reset(true); - m_lastPressTicks = 0; - m_lastTicks = 0; - // Release any DirectInput objects. - SAFE_RELEASE( m_pDI ); -} - -BOOL CALLBACK CJoystick::EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ) -{ - HRESULT hr; - CJoystick* p_this = (CJoystick*) pContext; - LPDIRECTINPUTDEVICE8 pJoystick = NULL; - - // Obtain an interface to the enumerated joystick. - hr = p_this->m_pDI->CreateDevice( pdidInstance->guidInstance, &pJoystick, NULL ); - if( SUCCEEDED( hr ) ) - { - // Set the data format to "simple joystick" - a predefined data format - // - // A data format specifies which controls on a device we are interested in, - // and how they should be reported. This tells DInput that we will be - // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState(). - if( SUCCEEDED( hr = pJoystick->SetDataFormat( &c_dfDIJoystick2 ) ) ) - { - // Set the cooperative level to let DInput know how this device should - // interact with the system and with other DInput applications. - if( SUCCEEDED( hr = pJoystick->SetCooperativeLevel( g_hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND ) ) ) - { - DIDEVCAPS diDevCaps; - diDevCaps.dwSize = sizeof(DIDEVCAPS); - if (SUCCEEDED(hr = pJoystick->GetCapabilities(&diDevCaps))) - { - CLog::Log(LOGNOTICE, __FUNCTION__" : Enabled Joystick: %s", pdidInstance->tszProductName); - CLog::Log(LOGNOTICE, __FUNCTION__" : Total Axis: %d Total Hats: %d Total Buttons: %d", diDevCaps.dwAxes, diDevCaps.dwPOVs, diDevCaps.dwButtons); - p_this->m_pJoysticks.push_back(pJoystick); - p_this->m_JoystickNames.push_back(pdidInstance->tszProductName); - p_this->m_devCaps.push_back(diDevCaps); - } - else - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to GetCapabilities for: %s", pdidInstance->tszProductName); - } - else - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to SetCooperativeLevel on: %s", pdidInstance->tszProductName); - - } - else - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to SetDataFormat on: %s", pdidInstance->tszProductName); - } - else - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to CreateDevice: %s", pdidInstance->tszProductName); - - return DIENUM_CONTINUE; -} - -//----------------------------------------------------------------------------- -// Name: EnumObjectsCallback() -// Desc: Callback function for enumerating objects (axes, buttons, POVs) on a -// joystick. This function enables user interface elements for objects -// that are found to exist, and scales axes min/max values. -//----------------------------------------------------------------------------- -BOOL CALLBACK CJoystick::EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ) -{ - - LPDIRECTINPUTDEVICE8 pJoy = (LPDIRECTINPUTDEVICE8) pContext; - - // For axes that are returned, set the DIPROP_RANGE property for the - // enumerated axis in order to scale min/max values. - if( pdidoi->dwType & DIDFT_AXIS ) - { - DIPROPRANGE diprg; - diprg.diph.dwSize = sizeof( DIPROPRANGE ); - diprg.diph.dwHeaderSize = sizeof( DIPROPHEADER ); - diprg.diph.dwHow = DIPH_BYID; - diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis - diprg.lMin = AXIS_MIN; - diprg.lMax = AXIS_MAX; - - // Set the range for the axis - if( FAILED( pJoy->SetProperty( DIPROP_RANGE, &diprg.diph ) ) ) - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to set property on %s", pdidoi->tszName); - } - - return DIENUM_CONTINUE; -} - -void CJoystick::Initialize() -{ - if (!IsEnabled()) - return; - - HRESULT hr; - - // clear old joystick names - ReleaseJoysticks(); - CSingleLock lock(m_critSection); - - if( FAILED( hr = DirectInput8Create( GetModuleHandle( NULL ), DIRECTINPUT_VERSION, IID_IDirectInput8, ( VOID** )&m_pDI, NULL ) ) ) - { - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to create DirectInput"); - return; - } - - if( FAILED( hr = m_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, this, DIEDFL_ATTACHEDONLY ) ) ) - return; - - if(m_pJoysticks.size() == 0) - { - CLog::Log(LOGDEBUG, __FUNCTION__" : No Joystick found"); - return; - } - - for(std::vector::iterator it = m_pJoysticks.begin(); it != m_pJoysticks.end(); ++it) - { - LPDIRECTINPUTDEVICE8 pJoy = (*it); - // Enumerate the joystick objects. The callback function enabled user - // interface elements for objects that are found, and sets the min/max - // values property for discovered axes. - if( FAILED( hr = pJoy->EnumObjects( EnumObjectsCallback, pJoy, DIDFT_ALL ) ) ) - CLog::Log(LOGDEBUG, __FUNCTION__" : Failed to enumerate objects"); - } - - m_JoyId = -1; - - // Set deadzone range - SetDeadzone(g_advancedSettings.m_controllerDeadzone); -} - -void CJoystick::Reset(bool axis /*=true*/) -{ - if (axis) - { - SetAxisActive(false); - for (int i = 0 ; i MAX_AXES) ? MAX_AXES : m_devCaps[j].dwAxes; - numhat = (m_devCaps[j].dwPOVs > 4) ? 4 : m_devCaps[j].dwPOVs; - - hr = pjoy->Poll(); - if( FAILED( hr ) ) - { - int i=0; - // DInput is telling us that the input stream has been - // interrupted. We aren't tracking any state between polls, so - // we don't have any special reset that needs to be done. We - // just re-acquire and try again. - hr = pjoy->Acquire(); - while( (hr == DIERR_INPUTLOST) && (i++ < 10) ) - hr = pjoy->Acquire(); - - // hr may be DIERR_OTHERAPPHASPRIO or other errors. This - // may occur when the app is minimized or in the process of - // switching, so just try again later - return; - } - - // Get the input's device state - if( FAILED( hr = pjoy->GetDeviceState( sizeof( DIJOYSTATE2 ), &js ) ) ) - return; // The device should have been acquired during the Poll() - - // get button states first, they take priority over axis - for( int b = 0; b < 128; b++ ) - { - if( js.rgbButtons[b] & 0x80 ) - { - m_JoyId = j; - buttonId = b+1; - j = numj-1; - break; - } - } - - // get hat position - m_HatState = SDL_HAT_CENTERED; - for (int h = 0; h < numhat; h++) - { - if((LOWORD(js.rgdwPOV[h]) == 0xFFFF) != true) - { - m_JoyId = j; - hatId = h + 1; - j = numj-1; - if ( (js.rgdwPOV[0] > JOY_POVLEFT) || (js.rgdwPOV[0] < JOY_POVRIGHT) ) - m_HatState |= SDL_HAT_UP; - - if ( (js.rgdwPOV[0] > JOY_POVFORWARD) && (js.rgdwPOV[0] < JOY_POVBACKWARD) ) - m_HatState |= SDL_HAT_RIGHT; - - if ( (js.rgdwPOV[0] > JOY_POVRIGHT) && (js.rgdwPOV[0] < JOY_POVLEFT) ) - m_HatState |= SDL_HAT_DOWN; - - if ( js.rgdwPOV[0] > JOY_POVBACKWARD ) - m_HatState |= SDL_HAT_LEFT; - break; - } - } - - // get axis states - m_Amount[0] = 0; - m_Amount[1] = js.lX; - m_Amount[2] = js.lY; - m_Amount[3] = js.lZ; - m_Amount[4] = js.lRx; - m_Amount[5] = js.lRy; - m_Amount[6] = js.lRz; - - m_AxisId = GetAxisWithMaxAmount(); - if (m_AxisId) - { - m_JoyId = j; - j = numj-1; - break; - } - } - - if(hatId==-1) - { - if(m_HatId!=0) - CLog::Log(LOGDEBUG, "Joystick %d hat %d Centered", m_JoyId, abs(hatId)); - m_pressTicksHat = 0; - SetHatActive(false); - m_HatId = 0; - } - else - { - if(hatId!=m_HatId) - { - CLog::Log(LOGDEBUG, "Joystick %d hat %u Down", m_JoyId, hatId); - m_HatId = hatId; - m_pressTicksHat = XbmcThreads::SystemClockMillis(); - } - SetHatActive(); - } - - if (buttonId==-1) - { - if (m_ButtonId!=0) - { - CLog::Log(LOGDEBUG, "Joystick %d button %d Up", m_JoyId, m_ButtonId); - } - m_pressTicksButton = 0; - SetButtonActive(false); - m_ButtonId = 0; - } - else - { - if (buttonId!=m_ButtonId) - { - CLog::Log(LOGDEBUG, "Joystick %d button %d Down", m_JoyId, buttonId); - m_ButtonId = buttonId; - m_pressTicksButton = XbmcThreads::SystemClockMillis(); - } - SetButtonActive(); - } - -} - -bool CJoystick::GetHat(int &id, int &position,bool consider_repeat) -{ - if (!IsEnabled() || !IsHatActive()) - { - id = position = 0; - return false; - } - position = m_HatState; - id = m_HatId; - if (!consider_repeat) - return true; - - uint32_t nowTicks = 0; - - if ((m_HatId>=0) && m_pressTicksHat) - { - // return the id if it's the first press - if (m_lastPressTicks!=m_pressTicksHat) - { - m_lastPressTicks = m_pressTicksHat; - return true; - } - nowTicks = XbmcThreads::SystemClockMillis(); - if ((nowTicks-m_pressTicksHat)<500) // 500ms delay before we repeat - return false; - if ((nowTicks-m_lastTicks)<100) // 100ms delay before successive repeats - return false; - - m_lastTicks = nowTicks; - } - - return true; -} - -bool CJoystick::GetButton(int &id, bool consider_repeat) -{ - if (!IsEnabled() || !IsButtonActive()) - { - id = 0; - return false; - } - if (!consider_repeat) - { - id = m_ButtonId; - return true; - } - - uint32_t nowTicks = 0; - - if ((m_ButtonId>=0) && m_pressTicksButton) - { - // return the id if it's the first press - if (m_lastPressTicks!=m_pressTicksButton) - { - m_lastPressTicks = m_pressTicksButton; - id = m_ButtonId; - return true; - } - nowTicks = XbmcThreads::SystemClockMillis(); - if ((nowTicks-m_pressTicksButton)<500) // 500ms delay before we repeat - { - return false; - } - if ((nowTicks-m_lastTicks)<100) // 100ms delay before successive repeats - { - return false; - } - m_lastTicks = nowTicks; - } - id = m_ButtonId; - return true; -} - -bool CJoystick::GetAxis (int &id) -{ - if (!IsEnabled() || !IsAxisActive()) - { - id = 0; - return false; - } - id = m_AxisId; - return true; -} - -int CJoystick::GetAxisWithMaxAmount() -{ - int maxAmount = 0; - int axis = 0; - int tempf; - for (int i = 1 ; i<=m_NumAxes ; i++) - { - tempf = abs(m_Amount[i]); - if (tempf>m_DeadzoneRange && tempf>maxAmount) - { - maxAmount = tempf; - axis = i; - } - } - SetAxisActive(0 != maxAmount); - return axis; -} - -float CJoystick::GetAmount(int axis) -{ - if (m_Amount[axis] > m_DeadzoneRange) - return (float)(m_Amount[axis]-m_DeadzoneRange)/(float)(MAX_AXISAMOUNT-m_DeadzoneRange); - if (m_Amount[axis] < -m_DeadzoneRange) - return (float)(m_Amount[axis]+m_DeadzoneRange)/(float)(MAX_AXISAMOUNT-m_DeadzoneRange); - return 0; -} - -void CJoystick::SetEnabled(bool enabled /*=true*/) -{ - if( enabled && !m_joystickEnabled ) - { - m_joystickEnabled = true; - Initialize(); - } - else if( !enabled && m_joystickEnabled ) - { - ReleaseJoysticks(); - m_joystickEnabled = false; - } -} - -float CJoystick::SetDeadzone(float val) -{ - if (val<0) val=0; - if (val>1) val=1; - m_DeadzoneRange = (int)(val*MAX_AXISAMOUNT); - return val; -} - -bool CJoystick::Reinitialize() -{ - Initialize(); - return true; -} - -void CJoystick::Acquire() -{ - if (!IsEnabled()) - return; - if(!m_pJoysticks.empty()) - { - CLog::Log(LOGDEBUG, __FUNCTION__": Focus back, acquire Joysticks"); - for(std::vector::iterator it = m_pJoysticks.begin(); it != m_pJoysticks.end(); ++it) - { - if( (*it) ) - (*it)->Acquire(); - } - } -} diff --git a/xbmc/input/windows/WINJoystick.h b/xbmc/input/windows/WINJoystick.h deleted file mode 100644 index b0232505fbe01..0000000000000 --- a/xbmc/input/windows/WINJoystick.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -/* -* Copyright (C) 2012-2013 Team XBMC - * http://xbmc.org - * - * This Program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This Program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with XBMC; see the file COPYING. If not, see - * . - * - */ - -#include -#include -#include -#include "settings/lib/ISettingCallback.h" -#include "threads/CriticalSection.h" - -#define JACTIVE_BUTTON 0x00000001 -#define JACTIVE_AXIS 0x00000002 -#define JACTIVE_HAT 0x00000004 -#define JACTIVE_NONE 0x00000000 -#define JACTIVE_HAT_UP 0x01 -#define JACTIVE_HAT_RIGHT 0x02 -#define JACTIVE_HAT_DOWN 0x04 -#define JACTIVE_HAT_LEFT 0x08 - -#define MAX_AXES 8 - -// Class to manage all connected joysticks - -class CJoystick : public ISettingCallback -{ -public: - CJoystick(); - ~CJoystick(); - - virtual void OnSettingChanged(const CSetting *setting); - - void Initialize(); - void Reset(bool axis=false); - void ResetAxis(int axisId) { m_Amount[axisId] = 0; } - void Update(); - bool GetButton (int& id, bool consider_repeat=true); - bool GetAxis (int &id); - bool GetHat (int &id, int &position, bool consider_repeat=true); - std::string GetJoystick() { return (m_JoyId>-1)?m_JoystickNames[m_JoyId]:""; } - int GetAxisWithMaxAmount(); - float GetAmount(int axis); - float GetAmount() { return GetAmount(m_AxisId); } - bool IsEnabled() const { return m_joystickEnabled; } - void SetEnabled(bool enabled = true); - float SetDeadzone(float val); - bool Reinitialize(); - void Acquire(); - -private: - void SetAxisActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_AXIS):(m_ActiveFlags&(~JACTIVE_AXIS)); } - void SetButtonActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_BUTTON):(m_ActiveFlags&(~JACTIVE_BUTTON)); } - void SetHatActive(bool active=true) { m_ActiveFlags = active?(m_ActiveFlags|JACTIVE_HAT):(m_ActiveFlags&(~JACTIVE_HAT)); } - bool IsButtonActive() { return (m_ActiveFlags & JACTIVE_BUTTON) == JACTIVE_BUTTON; } - bool IsAxisActive() { return (m_ActiveFlags & JACTIVE_AXIS) == JACTIVE_AXIS; } - bool IsHatActive() { return (m_ActiveFlags & JACTIVE_HAT) == JACTIVE_HAT; } - - void ReleaseJoysticks(); - static BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, VOID* pContext ); - static BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext ); - - int m_Amount[MAX_AXES]; - int m_AxisId; - int m_ButtonId; - uint8_t m_HatState; - int m_HatId; - int m_JoyId; - int m_NumAxes; - int m_DeadzoneRange; - bool m_joystickEnabled; - uint32_t m_pressTicksButton; - uint32_t m_pressTicksHat; - uint8_t m_ActiveFlags; - uint32_t m_lastPressTicks; - uint32_t m_lastTicks; - CCriticalSection m_critSection; - - LPDIRECTINPUT8 m_pDI; - std::vector m_pJoysticks; - std::vector m_JoystickNames; - std::vector m_devCaps; -}; - -extern CJoystick g_Joystick; diff --git a/xbmc/input/windows/WINJoystickDX.cpp b/xbmc/input/windows/WINJoystickDX.cpp new file mode 100644 index 0000000000000..bf7f4af64bfec --- /dev/null +++ b/xbmc/input/windows/WINJoystickDX.cpp @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "WINJoystickDX.h" +#include "system.h" +#include "utils/log.h" + +#include +#include + +// For getting the GUIDs of XInput devices +#include +#include + +extern HWND g_hWnd; + +#define MAX_AXISAMOUNT 32768 +#define AXIS_MIN -32768 /* minimum value for axis coordinate */ +#define AXIS_MAX 32767 /* maximum value for axis coordinate */ + +#define JOY_POV_360 JOY_POVBACKWARD * 2 +#define JOY_POV_NE (JOY_POVFORWARD + JOY_POVRIGHT) / 2 +#define JOY_POV_SE (JOY_POVRIGHT + JOY_POVBACKWARD) / 2 +#define JOY_POV_SW (JOY_POVBACKWARD + JOY_POVLEFT) / 2 +#define JOY_POV_NW (JOY_POVLEFT + JOY_POV_360) / 2 + +using namespace JOYSTICK; + +// A context to hold our DirectInput handle and accumulated joystick objects +struct JoystickEnumContext +{ + JoystickArray joystickItems; + LPDIRECTINPUT8 pDirectInput; +}; + +// DirectInput handle, we hold onto it and release it when freeing resources +LPDIRECTINPUT8 CJoystickDX::m_pDirectInput = NULL; + +CJoystickDX::CJoystickDX(LPDIRECTINPUTDEVICE8 joystickDevice, const std::string &name, const DIDEVCAPS &devCaps) + : m_joystickDevice(joystickDevice), m_state() +{ + // m_state.id is set in Initialize() before adding it to the joystick list + m_state.name = name; + m_state.ResetState(devCaps.dwButtons, devCaps.dwPOVs, devCaps.dwAxes); +} + +/* static */ +void CJoystickDX::Initialize(JoystickArray &joysticks) +{ + DeInitialize(joysticks); + + HRESULT hr; + JoystickEnumContext context; + + hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_pDirectInput, NULL); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to create DirectInput", __FUNCTION__); + return; + } + + context.pDirectInput = m_pDirectInput; + hr = context.pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, &context, DIEDFL_ATTACHEDONLY); + if (FAILED(hr) || context.joystickItems.size() == 0) + { + CLog::Log(LOGINFO, "%s: No joysticks found", __FUNCTION__); + return; + } + + for (JoystickArray::const_iterator it = context.joystickItems.begin(); it != context.joystickItems.end(); it++) + { + boost::shared_ptr jdx = boost::dynamic_pointer_cast(*it); + if (jdx && jdx->InitAxes()) + { + // Set the ID based on its position in the list + jdx->m_state.id = joysticks.size(); + joysticks.push_back(jdx); + } + } +} + +BOOL CALLBACK CJoystickDX::EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext) +{ + HRESULT hr; + const int result = DIENUM_CONTINUE; + + // Skip verified XInput devices + if (IsXInputDevice(&pdidInstance->guidProduct)) + return result; + + JoystickEnumContext *context = static_cast(pContext); + LPDIRECTINPUTDEVICE8 pJoystick = NULL; + + // Obtain an interface to the enumerated joystick. + hr = context->pDirectInput->CreateDevice(pdidInstance->guidInstance, &pJoystick, NULL); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to CreateDevice: %s", __FUNCTION__, pdidInstance->tszProductName); + return result; + } + + // Set the data format to "simple joystick" - a predefined data format. + // A data format specifies which controls on a device we are interested in, + // and how they should be reported. This tells DInput that we will be + // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState(). + hr = pJoystick->SetDataFormat(&c_dfDIJoystick2); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to SetDataFormat on: %s", __FUNCTION__, pdidInstance->tszProductName); + return result; + } + + // Set the cooperative level to let DInput know how this device should + // interact with the system and with other DInput applications. + hr = pJoystick->SetCooperativeLevel(g_hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to SetCooperativeLevel on: %s", __FUNCTION__, pdidInstance->tszProductName); + return result; + } + + DIDEVCAPS diDevCaps; + diDevCaps.dwSize = sizeof(DIDEVCAPS); + hr = pJoystick->GetCapabilities(&diDevCaps); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to GetCapabilities for: %s", __FUNCTION__, pdidInstance->tszProductName); + return result; + } + + CLog::Log(LOGNOTICE, "%s: Enabled Joystick: \"%s\" (DirectInput)", __FUNCTION__, pdidInstance->tszProductName); + CLog::Log(LOGNOTICE, "%s: Total Axes: %d Total Hats: %d Total Buttons: %d", __FUNCTION__, + diDevCaps.dwAxes, diDevCaps.dwPOVs, diDevCaps.dwButtons); + + CJoystickDX *joy = new CJoystickDX(pJoystick, pdidInstance->tszProductName, diDevCaps); + if (joy) + context->joystickItems.push_back(boost::shared_ptr(joy)); + + return result; +} + +//----------------------------------------------------------------------------- +// Enum each PNP device using WMI and check each device ID to see if it contains +// "IG_" (ex. "VID_045E&PID_028E&IG_00"). If it does, then it's an XInput device. +// Unfortunately this information can not be found by just using DirectInput. +// See http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx +//----------------------------------------------------------------------------- +/* static */ +bool CJoystickDX::IsXInputDevice(const GUID* pGuidProductFromDirectInput) +{ + IWbemLocator* pIWbemLocator = NULL; + IEnumWbemClassObject* pEnumDevices = NULL; + IWbemClassObject* pDevices[20] = {0}; + IWbemServices* pIWbemServices = NULL; + BSTR bstrNamespace = NULL; + BSTR bstrDeviceID = NULL; + BSTR bstrClassName = NULL; + DWORD uReturned = 0; + bool bIsXinputDevice = false; + UINT iDevice = 0; + VARIANT var; + HRESULT hr; + + // CoInit if needed + hr = CoInitialize(NULL); + bool bCleanupCOM = SUCCEEDED(hr); + + try + { + // Create WMI + hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*) &pIWbemLocator); + if (FAILED(hr) || pIWbemLocator == NULL) + throw hr; + + bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2"); + if (bstrNamespace == NULL) + throw hr; + + bstrClassName = SysAllocString(L"Win32_PNPEntity"); + if (bstrClassName == NULL) + throw hr; + + bstrDeviceID = SysAllocString(L"DeviceID"); + if (bstrDeviceID == NULL) + throw hr; + + // Connect to WMI + hr = pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices); + if (FAILED(hr) || pIWbemServices == NULL) + throw hr; + + // Switch security level to IMPERSONATE + CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE); + + hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, NULL, &pEnumDevices); + if (FAILED(hr) || pEnumDevices == NULL) + throw hr; + + // Loop over all devices + do + { + // Get 20 at a time + hr = pEnumDevices->Next(10000, 20, pDevices, &uReturned); + if (FAILED(hr)) + throw hr; + + for (iDevice = 0; iDevice < uReturned; iDevice++) + { + // Don't compare IDs if we already found our XInput device + if (!bIsXinputDevice) + { + // For each device, get its device ID + hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, NULL, NULL); + if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != NULL) + { + // Check if the device ID contains "IG_". If it does, then it's an XInput + // device. This information can not be found from DirectInput. + if (wcsstr(var.bstrVal, L"IG_")) + { + // If it does, then get the VID/PID from var.bstrVal + DWORD dwPid = 0; + DWORD dwVid = 0; + WCHAR *strVid = wcsstr(var.bstrVal, L"VID_"); + if (strVid && swscanf(strVid, L"VID_%4X", &dwVid) != 1) + dwVid = 0; + WCHAR* strPid = wcsstr(var.bstrVal, L"PID_"); + if (strPid && swscanf(strPid, L"PID_%4X", &dwPid) != 1) + dwPid = 0; + + // Compare the VID/PID to the DInput device + DWORD dwVidPid = MAKELONG(dwVid, dwPid); + if (dwVidPid == pGuidProductFromDirectInput->Data1) + bIsXinputDevice = true; + } + } + } + + SAFE_RELEASE(pDevices[iDevice]); + } + } + while (uReturned); + } + catch (HRESULT hr_error) + { + CLog::Log(LOGERROR, "%s: Error while testing for XInput device! hr=%ld", __FUNCTION__, hr_error); + } + + if (bstrNamespace) + SysFreeString(bstrNamespace); + if (bstrDeviceID) + SysFreeString(bstrDeviceID); + if (bstrClassName) + SysFreeString(bstrClassName); + for (iDevice = 0; iDevice < 20; iDevice++) + SAFE_RELEASE( pDevices[iDevice] ); + SAFE_RELEASE(pEnumDevices); + SAFE_RELEASE(pIWbemLocator); + SAFE_RELEASE(pIWbemServices); + + if (bCleanupCOM) + CoUninitialize(); + + return bIsXinputDevice; +} + +bool CJoystickDX::InitAxes() +{ + HRESULT hr; + + LPDIRECTINPUTDEVICE8 pJoy = m_joystickDevice; + + // Enumerate the joystick objects. The callback function enabled user + // interface elements for objects that are found, and sets the min/max + // values properly for discovered axes. + hr = pJoy->EnumObjects(EnumObjectsCallback, pJoy, DIDFT_ALL); + if (FAILED(hr)) + { + CLog::Log(LOGERROR, "%s: Failed to enumerate objects", __FUNCTION__); + return false; + } + return true; +} + +//----------------------------------------------------------------------------- +// Name: EnumObjectsCallback() +// Desc: Callback function for enumerating objects (axes, buttons, POVs) on a +// joystick. This function enables user interface elements for objects +// that are found to exist, and scales axes min/max values. +//----------------------------------------------------------------------------- +BOOL CALLBACK CJoystickDX::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* pContext) +{ + LPDIRECTINPUTDEVICE8 pJoy = static_cast(pContext); + + // For axes that are returned, set the DIPROP_RANGE property for the + // enumerated axis in order to scale min/max values. + if (pdidoi->dwType & DIDFT_AXIS) + { + DIPROPRANGE diprg; + diprg.diph.dwSize = sizeof(DIPROPRANGE); + diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER); + diprg.diph.dwHow = DIPH_BYID; + diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis + diprg.lMin = AXIS_MIN; + diprg.lMax = AXIS_MAX; + + // Set the range for the axis + HRESULT hr = pJoy->SetProperty(DIPROP_RANGE, &diprg.diph); + if (FAILED(hr)) + CLog::Log(LOGERROR, __FUNCTION__" : Failed to set property on %s", pdidoi->tszName); + } + return DIENUM_CONTINUE; +} + +void CJoystickDX::Release() +{ + // Unacquire the device one last time just in case the app tried to exit + // while the device is still acquired. + if (m_joystickDevice) + m_joystickDevice->Unacquire(); + SAFE_RELEASE(m_joystickDevice); +} + +/* static */ +void CJoystickDX::DeInitialize(JoystickArray &joysticks) +{ + for (int i = 0; i < (int)joysticks.size(); i++) + { + if (boost::dynamic_pointer_cast(joysticks[i])) + joysticks.erase(joysticks.begin() + i--); + } + // Release any DirectInput objects + SAFE_RELEASE(m_pDirectInput); +} + +void CJoystickDX::Update() +{ + HRESULT hr; + + LPDIRECTINPUTDEVICE8 pJoy = m_joystickDevice; + DIJOYSTATE2 js; // DInput joystick state + + hr = pJoy->Poll(); + + if (FAILED(hr)) + { + int i = 0; + // DInput is telling us that the input stream has been interrupted. We + // aren't tracking any state between polls, so we don't have any special + // reset that needs to be done. We just re-acquire and try again 10 times. + do + { + hr = pJoy->Acquire(); + } + while (hr == DIERR_INPUTLOST && i++ < 10); + + // hr may be DIERR_OTHERAPPHASPRIO or other errors. This may occur when the + // app is minimized or in the process of switching, so just try again later. + return; + } + + // Get the input's device state + hr = pJoy->GetDeviceState(sizeof(DIJOYSTATE2), &js); + if (FAILED(hr)) + return; // The device should have been acquired during the Poll() + + // Gamepad buttons + for (unsigned int b = 0; b < m_state.buttons.size(); b++) + m_state.buttons[b] = ((js.rgbButtons[b] & 0x80) ? 1: 0); + + // Gamepad hats + for (unsigned int h = 0; h < m_state.hats.size(); h++) + { + m_state.hats[h].Center(); + bool bCentered = ((js.rgdwPOV[h] & 0xFFFF) == 0xFFFF); + if (!bCentered) + { + if ((JOY_POV_NW <= js.rgdwPOV[h] && js.rgdwPOV[h] <= JOY_POV_360) || js.rgdwPOV[h] <= JOY_POV_NE) + m_state.hats[h].up = true; + else if (JOY_POV_SE <= js.rgdwPOV[h] && js.rgdwPOV[h] <= JOY_POV_SW) + m_state.hats[h].down = true; + + if (JOY_POV_NE <= js.rgdwPOV[h] && js.rgdwPOV[h] <= JOY_POV_SE) + m_state.hats[h].right = true; + else if (JOY_POV_SW <= js.rgdwPOV[h] && js.rgdwPOV[h] <= JOY_POV_NW) + m_state.hats[h].left = true; + } + } + + // Gamepad axes + long amounts[] = {js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz}; + for (unsigned int a = 0; a < std::min(m_state.axes.size(), 6U); a++) + m_state.SetAxis(a, amounts[a], MAX_AXISAMOUNT); +} diff --git a/xbmc/input/windows/WINJoystickDX.h b/xbmc/input/windows/WINJoystickDX.h new file mode 100644 index 0000000000000..9e41e351f6200 --- /dev/null +++ b/xbmc/input/windows/WINJoystickDX.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#pragma once + +#include "input/IJoystick.h" + +#include + +namespace JOYSTICK +{ + +class CJoystickDX : public IJoystick +{ +public: + static void Initialize(JoystickArray &joysticks); + static void DeInitialize(JoystickArray &joysticks); + + virtual ~CJoystickDX() { Release(); } + virtual void Update(); + virtual const Joystick &GetState() const { return m_state; } + +private: + CJoystickDX(LPDIRECTINPUTDEVICE8 joystickDevice, const std::string &name, const DIDEVCAPS &devCaps); + + static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE *pdidInstance, VOID *pContext); + static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE *pdidoi, VOID *pContext); + static bool IsXInputDevice(const GUID *pGuidProductFromDirectInput); + bool InitAxes(); + void Release(); + + static LPDIRECTINPUT8 m_pDirectInput; + LPDIRECTINPUTDEVICE8 m_joystickDevice; + Joystick m_state; +}; + +} // namespace JOYSTICK diff --git a/xbmc/input/windows/WINJoystickXInput.cpp b/xbmc/input/windows/WINJoystickXInput.cpp new file mode 100644 index 0000000000000..aa4f1762f6d8b --- /dev/null +++ b/xbmc/input/windows/WINJoystickXInput.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2012-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "WINJoystickXInput.h" +#include "system.h" +#include "utils/log.h" + +#include +#include + +#pragma comment(lib, "XInput.lib") + +#define XINPUT_ALIAS "XBMC-Compatible XInput Controller" +#define MAX_JOYSTICKS 4 +#define MAX_AXIS 32768 +#define MAX_TRIGGER 255 +#define BUTTON_COUNT 10 +#define HAT_COUNT 1 +#define AXIS_COUNT 5 + +using namespace JOYSTICK; + +CJoystickXInput::CJoystickXInput(unsigned int controllerID, unsigned int id) + : m_state(), m_controllerID(controllerID), m_dwPacketNumber(0) +{ + m_state.id = id; + m_state.name = XINPUT_ALIAS; + m_state.ResetState(BUTTON_COUNT, HAT_COUNT, AXIS_COUNT); +} + +/* static */ +void CJoystickXInput::Initialize(JoystickArray &joysticks) +{ + DeInitialize(joysticks); + + XINPUT_STATE controllerState; // No need to memset, only checking for controller existence + + for (unsigned int i = 0; i < MAX_JOYSTICKS; i++) + { + DWORD result = XInputGetState(i, &controllerState); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + CLog::Log(LOGNOTICE, "CJoystickXInput: No XInput devices on port %u", i); + continue; + } + + // That's all it takes to check controller existence... I <3 XInput + CLog::Log(LOGNOTICE, "CJoystickXInput: Found a 360-compatible XInput controller on port %u", i); + joysticks.push_back(boost::shared_ptr(new CJoystickXInput(i, joysticks.size()))); + } +} + +/* static */ +void CJoystickXInput::DeInitialize(JoystickArray &joysticks) +{ + for (int i = 0; i < (int)joysticks.size(); i++) + { + if (boost::dynamic_pointer_cast(joysticks[i])) + joysticks.erase(joysticks.begin() + i--); + } +} + +void CJoystickXInput::Update() +{ + XINPUT_STATE controllerState; + + DWORD result = XInputGetState(m_controllerID, &controllerState); + if (result != ERROR_SUCCESS) + return; + + if (m_dwPacketNumber == controllerState.dwPacketNumber) + return; // No update since last poll + m_dwPacketNumber = controllerState.dwPacketNumber; + + // Map to DirectInput controls + m_state.buttons[0] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_A) ? true : false; + m_state.buttons[1] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_B) ? true : false; + m_state.buttons[2] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_X) ? true : false; + m_state.buttons[3] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_Y) ? true : false; + m_state.buttons[4] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? true : false; + m_state.buttons[5] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) ? true : false; + m_state.buttons[6] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_BACK) ? true : false; + m_state.buttons[7] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_START) ? true : false; + m_state.buttons[8] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB) ? true : false; + m_state.buttons[9] = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) ? true : false; + + m_state.hats[0].up = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) ? true : false; + m_state.hats[0].right = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) ? true : false; + m_state.hats[0].down = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) ? true : false; + m_state.hats[0].left = (controllerState.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) ? true : false; + + // Combine triggers into a single axis, like DirectInput + const long triggerAxis = (long)controllerState.Gamepad.bLeftTrigger - (long)controllerState.Gamepad.bRightTrigger; + m_state.SetAxis(0, controllerState.Gamepad.sThumbLX, MAX_AXIS); + m_state.SetAxis(1, -controllerState.Gamepad.sThumbLY, MAX_AXIS); + m_state.SetAxis(2, triggerAxis, MAX_TRIGGER); + m_state.SetAxis(3, controllerState.Gamepad.sThumbRX, MAX_AXIS); + m_state.SetAxis(4, -controllerState.Gamepad.sThumbRY, MAX_AXIS); +} diff --git a/xbmc/input/windows/WINJoystickXInput.h b/xbmc/input/windows/WINJoystickXInput.h new file mode 100644 index 0000000000000..a597b294c3153 --- /dev/null +++ b/xbmc/input/windows/WINJoystickXInput.h @@ -0,0 +1,47 @@ +#pragma once +/* + * Copyright (C) 2012-2013 Team XBMC + * http://www.xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +#include "input/IJoystick.h" + +union SDL_Event; + +namespace JOYSTICK +{ + +class CJoystickXInput : public IJoystick +{ +public: + static void Initialize(JoystickArray &joysticks); + static void DeInitialize(JoystickArray &joysticks); + + virtual ~CJoystickXInput() { } + virtual void Update(); + virtual const Joystick &GetState() const { return m_state; } + +private: + CJoystickXInput(unsigned int controllerID, unsigned int id); + + Joystick m_state; + unsigned int m_controllerID; // XInput port, in the range (0, 3) + DWORD m_dwPacketNumber; // If unchanged, controller state hasn't changed +}; + +} // namespace JOYSTICK diff --git a/xbmc/linux/HALManager.cpp b/xbmc/linux/HALManager.cpp index cfb699c291404..06f7b7cab3fce 100644 --- a/xbmc/linux/HALManager.cpp +++ b/xbmc/linux/HALManager.cpp @@ -33,7 +33,9 @@ #ifdef HAS_SDL_JOYSTICK #include #include -#include "input/SDLJoystick.h" +#include "input/JoystickManager.h" + +using namespace JOYSTICK; #endif bool CHALManager::NewMessage; @@ -512,8 +514,7 @@ void CHALManager::AddDevice(const char *udi) if(m_Joysticks.size() < 2 || m_bMultipleJoysticksSupport) { // Restart SDL joystick subsystem - if (!g_Joystick.Reinitialize()) - break; + CJoystickManager::Get().Reinitialize(); if (m_Notifications) CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, g_localizeStrings.Get(13024), dev.FriendlyName.c_str(), TOAST_DISPLAY_TIME, false); @@ -567,8 +568,7 @@ bool CHALManager::RemoveDevice(const char *udi) if(m_Joysticks.size() < 3 || m_bMultipleJoysticksSupport) { // Restart SDL joystick subsystem - if (!g_Joystick.Reinitialize()) - return false; + CJoystickManager::Get().Reinitialize(); if (m_Notifications) CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, g_localizeStrings.Get(13025), m_Joysticks[i].FriendlyName.c_str(), TOAST_DISPLAY_TIME, false); diff --git a/xbmc/peripherals/devices/PeripheralImon.cpp b/xbmc/peripherals/devices/PeripheralImon.cpp index d42d647589e70..f636fab002c1c 100644 --- a/xbmc/peripherals/devices/PeripheralImon.cpp +++ b/xbmc/peripherals/devices/PeripheralImon.cpp @@ -24,10 +24,10 @@ #include "settings/Settings.h" #include "threads/Atomics.h" #if defined (TARGET_WINDOWS) -#include "system.h" // For HAS_SDL_JOYSTICK -#if defined (HAS_SDL_JOYSTICK) -#include "input/windows/WINJoystick.h" -#endif // HAS_SDL_JOYSTICK +#include "system.h" // For HAS_JOYSTICK +#if defined (HAS_JOYSTICK) +#include "input/JoystickManager.h" +#endif // HAS_JOYSTICK #endif // TARGET_WINDOWS @@ -106,11 +106,11 @@ void CPeripheralImon::ActionOnImonConflict(bool deviceInserted /*= true*/) { if (deviceInserted || m_lCountOfImonsConflictWithDInput == 0) { -#if defined(TARGET_WINDOWS) && defined (HAS_SDL_JOYSTICK) +#if defined(TARGET_WINDOWS) && defined(HAS_JOYSTICK) bool enableJoystickNow = !deviceInserted && CSettings::Get().GetBool("input.enablejoystick"); CLog::Log(LOGNOTICE, "Problematic iMON hardware %s. Joystick usage: %s", (deviceInserted ? "detected" : "was removed"), (enableJoystickNow) ? "enabled." : "disabled." ); - g_Joystick.SetEnabled(enableJoystickNow); + JOYSTICK::CJoystickManager::Get().SetEnabled(enableJoystickNow); #endif } } diff --git a/xbmc/powermanagement/PowerManager.cpp b/xbmc/powermanagement/PowerManager.cpp index 590a88723dbad..5c89e144f94c2 100644 --- a/xbmc/powermanagement/PowerManager.cpp +++ b/xbmc/powermanagement/PowerManager.cpp @@ -35,6 +35,11 @@ #include "guilib/GUIWindowManager.h" #include "dialogs/GUIDialogBusy.h" #include "dialogs/GUIDialogKaiToast.h" +#ifdef HAS_JOYSTICK +#include "input/JoystickManager.h" +#include "peripherals/devices/PeripheralImon.h" +#endif + #if defined(TARGET_DARWIN) #include "osx/CocoaPowerSyscall.h" @@ -57,6 +62,9 @@ extern HWND g_hWnd; #endif using namespace ANNOUNCEMENT; +#if defined(HAS_JOYSTICK) +using namespace JOYSTICK; +#endif // HAS_JOYSTICK CPowerManager g_powerManager; @@ -243,6 +251,11 @@ void CPowerManager::OnSleep() CBuiltins::Execute("LIRC.Stop"); #endif +#ifdef HAS_JOYSTICK + CLog::Log(LOGNOTICE, "%s: Stopping joystick manager", __FUNCTION__); + CJoystickManager::Get().SetEnabled(false); +#endif + g_application.SaveFileState(true); g_application.StopPlaying(); g_application.StopShutdownTimer(); @@ -284,6 +297,12 @@ void CPowerManager::OnWake() CBuiltins::Execute("LIRC.Start"); #endif +#ifdef HAS_JOYSTICK + CLog::Log(LOGNOTICE, "%s: Restarting joystick manager", __FUNCTION__); + CJoystickManager::Get().SetEnabled(CSettings::Get().GetBool("input.enablejoystick") && + PERIPHERALS::CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0); +#endif + CAEFactory::Resume(); g_application.UpdateLibraries(); g_weatherManager.Refresh(); diff --git a/xbmc/settings/Settings.cpp b/xbmc/settings/Settings.cpp index 892a1392c8f59..233d7dcefe545 100644 --- a/xbmc/settings/Settings.cpp +++ b/xbmc/settings/Settings.cpp @@ -42,11 +42,9 @@ #include "guilib/LocalizeStrings.h" #include "guilib/StereoscopicsManager.h" #include "input/MouseStat.h" -#if defined(TARGET_WINDOWS) -#include "input/windows/WINJoystick.h" -#elif defined(HAS_SDL_JOYSTICK) -#include "input/SDLJoystick.h" -#endif // defined(HAS_SDL_JOYSTICK) +#if defined(HAS_JOYSTICK) +#include "input/JoystickManager.h" +#endif // defined(HAS_JOYSTICK) #if defined(TARGET_POSIX) #include "linux/LinuxTimezone.h" #endif // defined(TARGET_POSIX) @@ -60,6 +58,7 @@ #include "osx/DarwinUtils.h" #endif #include "peripherals/Peripherals.h" +#include "peripherals/devices/PeripheralImon.h" #include "powermanagement/PowerManager.h" #include "profiles/ProfilesManager.h" #include "pvr/PVRManager.h" @@ -130,6 +129,11 @@ bool HasPeripherals(const std::string &condition, const std::string &value, cons return PERIPHERALS::g_peripherals.GetNumberOfPeripherals() > 0; } +bool HasImonsConflict(const std::string &condition, const std::string &value, const std::string &settingId) +{ + return PERIPHERALS::CPeripheralImon::GetCountOfImonsConflictWithDInput() != 0; +} + bool IsFullscreen(const std::string &condition, const std::string &value, const std::string &settingId) { return g_Windowing.IsFullScreen(); @@ -429,8 +433,8 @@ void CSettings::Uninitialize() m_settingsManager->UnregisterCallback(&g_charsetConverter); m_settingsManager->UnregisterCallback(&g_graphicsContext); m_settingsManager->UnregisterCallback(&g_langInfo); -#if defined(TARGET_WINDOWS) || defined(HAS_SDL_JOYSTICK) - m_settingsManager->UnregisterCallback(&g_Joystick); +#if defined(HAS_JOYSTICK) + m_settingsManager->UnregisterCallback(&JOYSTICK::CJoystickManager::Get()); #endif m_settingsManager->UnregisterCallback(&g_Mouse); m_settingsManager->UnregisterCallback(&CNetworkServices::Get()); @@ -874,8 +878,8 @@ void CSettings::InitializeConditions() #ifdef HAS_KARAOKE m_settingsManager->AddCondition("has_karaoke"); #endif -#ifdef HAS_SDL_JOYSTICK - m_settingsManager->AddCondition("has_sdl_joystick"); +#ifdef HAS_JOYSTICK + m_settingsManager->AddCondition("has_joystick"); #endif #ifdef HAS_SKIN_TOUCHED m_settingsManager->AddCondition("has_skin_touched"); @@ -939,6 +943,7 @@ void CSettings::InitializeConditions() m_settingsManager->AddCondition("checkmasterlock", CheckMasterLock); m_settingsManager->AddCondition("checkpvrparentalpin", CheckPVRParentalPin); m_settingsManager->AddCondition("hasperipherals", HasPeripherals); + m_settingsManager->AddCondition("hasimonsconflict", HasImonsConflict); m_settingsManager->AddCondition("isfullscreen", IsFullscreen); m_settingsManager->AddCondition("ismasteruser", IsMasterUser); m_settingsManager->AddCondition("isusingttfsubtitles", IsUsingTTFSubtitles); @@ -1082,10 +1087,10 @@ void CSettings::InitializeISettingCallbacks() settingSet.insert("locale.country"); m_settingsManager->RegisterCallback(&g_langInfo, settingSet); -#if defined(HAS_SDL_JOYSTICK) +#if defined(HAS_JOYSTICK) settingSet.clear(); settingSet.insert("input.enablejoystick"); - m_settingsManager->RegisterCallback(&g_Joystick, settingSet); + m_settingsManager->RegisterCallback(&JOYSTICK::CJoystickManager::Get(), settingSet); #endif settingSet.clear(); diff --git a/xbmc/system.h b/xbmc/system.h index 1f4e15f55fcd8..0fadbac9368ff 100644 --- a/xbmc/system.h +++ b/xbmc/system.h @@ -89,6 +89,10 @@ #define HAS_UPNP #endif +#if defined(HAS_SDL_JOYSTICK) || defined(HAS_LINUX_JOYSTICK) + #define HAS_JOYSTICK +#endif + #if defined(HAVE_LIBMDNSEMBEDDED) #define HAS_ZEROCONF #define HAS_MDNS @@ -112,7 +116,7 @@ *****************/ #if defined(TARGET_WINDOWS) -#define HAS_SDL_JOYSTICK +#define HAS_JOYSTICK #define HAS_DVD_DRIVE #define HAS_WIN32_NETWORK #define HAS_IRSERVERSUITE diff --git a/xbmc/windowing/WinEventsSDL.cpp b/xbmc/windowing/WinEventsSDL.cpp index 4713cdbf50772..72a4e1febc84c 100644 --- a/xbmc/windowing/WinEventsSDL.cpp +++ b/xbmc/windowing/WinEventsSDL.cpp @@ -29,9 +29,6 @@ #include "settings/DisplaySettings.h" #include "guilib/GUIWindowManager.h" #include "guilib/Key.h" -#ifdef HAS_SDL_JOYSTICK -#include "input/SDLJoystick.h" -#endif #include "input/MouseStat.h" #include "WindowingFactory.h" #if defined(TARGET_DARWIN) @@ -227,17 +224,6 @@ bool CWinEventsSDL::MessagePump() CApplicationMessenger::Get().Quit(); break; -#ifdef HAS_SDL_JOYSTICK - case SDL_JOYBUTTONUP: - case SDL_JOYBUTTONDOWN: - case SDL_JOYAXISMOTION: - case SDL_JOYBALLMOTION: - case SDL_JOYHATMOTION: - g_Joystick.Update(event); - ret = true; - break; -#endif - case SDL_ACTIVEEVENT: //If the window was inconified or restored if( event.active.state & SDL_APPACTIVE ) diff --git a/xbmc/windowing/windows/WinEventsWin32.cpp b/xbmc/windowing/windows/WinEventsWin32.cpp index e3a46387b02aa..2a4d6387c23f9 100644 --- a/xbmc/windowing/windows/WinEventsWin32.cpp +++ b/xbmc/windowing/windows/WinEventsWin32.cpp @@ -33,7 +33,7 @@ #include "input/MouseStat.h" #include "input/touch/generic/GenericTouchActionHandler.h" #include "input/touch/generic/GenericTouchSwipeDetector.h" -#include "input/windows/WINJoystick.h" +#include "input/JoystickManager.h" #include "storage/MediaManager.h" #include "windowing/WindowingFactory.h" #include @@ -55,6 +55,7 @@ #ifdef TARGET_WINDOWS +using namespace JOYSTICK; using namespace PERIPHERALS; HWND g_hWnd = NULL; @@ -451,7 +452,7 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_ACTIVATE: { if( WA_INACTIVE != wParam ) - g_Joystick.Reinitialize(); + CJoystickManager::Get().Reinitialize(); bool active = g_application.GetRenderGUI(); if (HIWORD(wParam)) @@ -760,7 +761,7 @@ LRESULT CALLBACK CWinEventsWin32::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, L if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { g_peripherals.TriggerDeviceScan(PERIPHERAL_BUS_USB); - g_Joystick.Reinitialize(); + CJoystickManager::Get().Reinitialize(); } // check if an usb or optical media was inserted or removed if (((_DEV_BROADCAST_HEADER*) lParam)->dbcd_devicetype == DBT_DEVTYP_VOLUME)