1 /*
2  *  Copyright (C) 2014-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PeripheralJoystick.h"
10 
11 #include "Application.h"
12 #include "games/controllers/ControllerIDs.h"
13 #include "input/InputManager.h"
14 #include "input/joysticks/DeadzoneFilter.h"
15 #include "input/joysticks/JoystickMonitor.h"
16 #include "input/joysticks/JoystickTranslator.h"
17 #include "input/joysticks/RumbleGenerator.h"
18 #include "input/joysticks/interfaces/IDriverHandler.h"
19 #include "input/joysticks/keymaps/KeymapHandling.h"
20 #include "peripherals/Peripherals.h"
21 #include "peripherals/addons/AddonButtonMap.h"
22 #include "peripherals/bus/virtual/PeripheralBusAddon.h"
23 #include "threads/SingleLock.h"
24 #include "utils/log.h"
25 
26 #include <algorithm>
27 
28 using namespace KODI;
29 using namespace JOYSTICK;
30 using namespace PERIPHERALS;
31 
CPeripheralJoystick(CPeripherals & manager,const PeripheralScanResult & scanResult,CPeripheralBus * bus)32 CPeripheralJoystick::CPeripheralJoystick(CPeripherals& manager,
33                                          const PeripheralScanResult& scanResult,
34                                          CPeripheralBus* bus)
35   : CPeripheral(manager, scanResult, bus),
36     m_requestedPort(JOYSTICK_PORT_UNKNOWN),
37     m_buttonCount(0),
38     m_hatCount(0),
39     m_axisCount(0),
40     m_motorCount(0),
41     m_supportsPowerOff(false),
42     m_rumbleGenerator(new CRumbleGenerator)
43 {
44   m_features.push_back(FEATURE_JOYSTICK);
45   // FEATURE_RUMBLE conditionally added via SetMotorCount()
46 }
47 
~CPeripheralJoystick(void)48 CPeripheralJoystick::~CPeripheralJoystick(void)
49 {
50   if (m_rumbleGenerator)
51   {
52     m_rumbleGenerator->AbortRumble();
53     m_rumbleGenerator.reset();
54   }
55 
56   if (m_joystickMonitor)
57   {
58     UnregisterInputHandler(m_joystickMonitor.get());
59     m_joystickMonitor.reset();
60   }
61 
62   m_appInput.reset();
63   m_deadzoneFilter.reset();
64   m_buttonMap.reset();
65 }
66 
InitialiseFeature(const PeripheralFeature feature)67 bool CPeripheralJoystick::InitialiseFeature(const PeripheralFeature feature)
68 {
69   bool bSuccess = false;
70 
71   if (CPeripheral::InitialiseFeature(feature))
72   {
73     if (feature == FEATURE_JOYSTICK)
74     {
75       // Ensure an add-on is present to translate input
76       if (!m_manager.GetAddonWithButtonMap(this))
77       {
78         CLog::Log(LOGERROR, "CPeripheralJoystick: No button mapping add-on for %s",
79                   m_strLocation.c_str());
80       }
81       else
82       {
83         if (m_bus->InitializeProperties(*this))
84           bSuccess = true;
85         else
86           CLog::Log(LOGERROR, "CPeripheralJoystick: Invalid location (%s)", m_strLocation.c_str());
87       }
88 
89       if (bSuccess)
90       {
91         InitializeDeadzoneFiltering();
92 
93         // Give joystick monitor priority over default controller
94         m_appInput.reset(
95             new CKeymapHandling(this, false, m_manager.GetInputManager().KeymapEnvironment()));
96         m_joystickMonitor.reset(new CJoystickMonitor);
97         RegisterInputHandler(m_joystickMonitor.get(), false);
98       }
99     }
100     else if (feature == FEATURE_RUMBLE)
101     {
102       bSuccess = true; // Nothing to do
103     }
104     else if (feature == FEATURE_POWER_OFF)
105     {
106       bSuccess = true; // Nothing to do
107     }
108   }
109 
110   return bSuccess;
111 }
112 
InitializeDeadzoneFiltering()113 void CPeripheralJoystick::InitializeDeadzoneFiltering()
114 {
115   // Get a button map for deadzone filtering
116   PeripheralAddonPtr addon = m_manager.GetAddonWithButtonMap(this);
117   if (addon)
118   {
119     m_buttonMap.reset(new CAddonButtonMap(this, addon, DEFAULT_CONTROLLER_ID));
120     if (m_buttonMap->Load())
121     {
122       m_deadzoneFilter.reset(new CDeadzoneFilter(m_buttonMap.get(), this));
123     }
124     else
125     {
126       CLog::Log(LOGERROR,
127                 "CPeripheralJoystick: Failed to load button map for deadzone filtering on %s",
128                 m_strLocation.c_str());
129       m_buttonMap.reset();
130     }
131   }
132   else
133   {
134     CLog::Log(LOGERROR,
135               "CPeripheralJoystick: Failed to create button map for deadzone filtering on %s",
136               m_strLocation.c_str());
137   }
138 }
139 
OnUserNotification()140 void CPeripheralJoystick::OnUserNotification()
141 {
142   IInputReceiver* inputReceiver = m_appInput->GetInputReceiver(m_rumbleGenerator->ControllerID());
143   m_rumbleGenerator->NotifyUser(inputReceiver);
144 }
145 
TestFeature(PeripheralFeature feature)146 bool CPeripheralJoystick::TestFeature(PeripheralFeature feature)
147 {
148   bool bSuccess = false;
149 
150   switch (feature)
151   {
152     case FEATURE_RUMBLE:
153     {
154       IInputReceiver* inputReceiver =
155           m_appInput->GetInputReceiver(m_rumbleGenerator->ControllerID());
156       bSuccess = m_rumbleGenerator->DoTest(inputReceiver);
157       break;
158     }
159     case FEATURE_POWER_OFF:
160       if (m_supportsPowerOff)
161       {
162         PowerOff();
163         bSuccess = true;
164       }
165       break;
166     default:
167       break;
168   }
169 
170   return bSuccess;
171 }
172 
PowerOff()173 void CPeripheralJoystick::PowerOff()
174 {
175   m_bus->PowerOff(m_strLocation);
176 }
177 
RegisterJoystickDriverHandler(IDriverHandler * handler,bool bPromiscuous)178 void CPeripheralJoystick::RegisterJoystickDriverHandler(IDriverHandler* handler, bool bPromiscuous)
179 {
180   CSingleLock lock(m_handlerMutex);
181 
182   DriverHandler driverHandler = {handler, bPromiscuous};
183   m_driverHandlers.insert(m_driverHandlers.begin(), driverHandler);
184 }
185 
UnregisterJoystickDriverHandler(IDriverHandler * handler)186 void CPeripheralJoystick::UnregisterJoystickDriverHandler(IDriverHandler* handler)
187 {
188   CSingleLock lock(m_handlerMutex);
189 
190   m_driverHandlers.erase(std::remove_if(m_driverHandlers.begin(), m_driverHandlers.end(),
191                                         [handler](const DriverHandler& driverHandler) {
192                                           return driverHandler.handler == handler;
193                                         }),
194                          m_driverHandlers.end());
195 }
196 
GetKeymap(const std::string & controllerId)197 IKeymap* CPeripheralJoystick::GetKeymap(const std::string& controllerId)
198 {
199   return m_appInput->GetKeymap(controllerId);
200 }
201 
OnButtonMotion(unsigned int buttonIndex,bool bPressed)202 bool CPeripheralJoystick::OnButtonMotion(unsigned int buttonIndex, bool bPressed)
203 {
204   // Silence debug log if controllers are not enabled
205   if (m_manager.GetInputManager().IsControllerEnabled())
206   {
207     CLog::Log(LOGDEBUG, "BUTTON [ %u ] on \"%s\" %s", buttonIndex, DeviceName().c_str(),
208               bPressed ? "pressed" : "released");
209   }
210 
211   // Avoid sending activated input if the app is in the background
212   if (bPressed && !g_application.IsAppFocused())
213     return false;
214 
215   m_lastActive = CDateTime::GetCurrentDateTime();
216 
217   CSingleLock lock(m_handlerMutex);
218 
219   // Check GUI setting and send button release if controllers are disabled
220   if (!m_manager.GetInputManager().IsControllerEnabled())
221   {
222     for (std::vector<DriverHandler>::iterator it = m_driverHandlers.begin();
223          it != m_driverHandlers.end(); ++it)
224       it->handler->OnButtonMotion(buttonIndex, false);
225     return true;
226   }
227 
228   // Process promiscuous handlers
229   for (auto& it : m_driverHandlers)
230   {
231     if (it.bPromiscuous)
232       it.handler->OnButtonMotion(buttonIndex, bPressed);
233   }
234 
235   bool bHandled = false;
236 
237   // Process regular handlers until one is handled
238   for (auto& it : m_driverHandlers)
239   {
240     if (!it.bPromiscuous)
241     {
242       bHandled |= it.handler->OnButtonMotion(buttonIndex, bPressed);
243 
244       // If button is released, force bHandled to false to notify all handlers.
245       // This avoids "sticking".
246       if (!bPressed)
247         bHandled = false;
248 
249       // Once a button is handled, we're done
250       if (bHandled)
251         break;
252     }
253   }
254 
255   return bHandled;
256 }
257 
OnHatMotion(unsigned int hatIndex,HAT_STATE state)258 bool CPeripheralJoystick::OnHatMotion(unsigned int hatIndex, HAT_STATE state)
259 {
260   // Silence debug log if controllers are not enabled
261   if (m_manager.GetInputManager().IsControllerEnabled())
262   {
263     CLog::Log(LOGDEBUG, "HAT [ %u ] on \"%s\" %s", hatIndex, DeviceName().c_str(),
264               CJoystickTranslator::HatStateToString(state));
265   }
266 
267   // Avoid sending activated input if the app is in the background
268   if (state != HAT_STATE::NONE && !g_application.IsAppFocused())
269     return false;
270 
271   m_lastActive = CDateTime::GetCurrentDateTime();
272 
273   CSingleLock lock(m_handlerMutex);
274 
275   // Check GUI setting and send hat unpressed if controllers are disabled
276   if (!m_manager.GetInputManager().IsControllerEnabled())
277   {
278     for (std::vector<DriverHandler>::iterator it = m_driverHandlers.begin();
279          it != m_driverHandlers.end(); ++it)
280       it->handler->OnHatMotion(hatIndex, HAT_STATE::NONE);
281     return true;
282   }
283 
284   // Process promiscuous handlers
285   for (auto& it : m_driverHandlers)
286   {
287     if (it.bPromiscuous)
288       it.handler->OnHatMotion(hatIndex, state);
289   }
290 
291   bool bHandled = false;
292 
293   // Process regular handlers until one is handled
294   for (auto& it : m_driverHandlers)
295   {
296     if (!it.bPromiscuous)
297     {
298       bHandled |= it.handler->OnHatMotion(hatIndex, state);
299 
300       // If hat is centered, force bHandled to false to notify all handlers.
301       // This avoids "sticking".
302       if (state == HAT_STATE::NONE)
303         bHandled = false;
304 
305       // Once a hat is handled, we're done
306       if (bHandled)
307         break;
308     }
309   }
310 
311   return bHandled;
312 }
313 
OnAxisMotion(unsigned int axisIndex,float position)314 bool CPeripheralJoystick::OnAxisMotion(unsigned int axisIndex, float position)
315 {
316   // Get axis properties
317   int center = 0;
318   unsigned int range = 1;
319   if (m_buttonMap)
320     m_buttonMap->GetAxisProperties(axisIndex, center, range);
321 
322   // Apply deadzone filtering
323   if (center == 0 && m_deadzoneFilter)
324     position = m_deadzoneFilter->FilterAxis(axisIndex, position);
325 
326   // Avoid sending activated input if the app is in the background
327   if (position != static_cast<float>(center) && !g_application.IsAppFocused())
328     return false;
329 
330   CSingleLock lock(m_handlerMutex);
331 
332   // Check GUI setting and send analog axis centered if controllers are disabled
333   if (!m_manager.GetInputManager().IsControllerEnabled())
334   {
335     for (std::vector<DriverHandler>::iterator it = m_driverHandlers.begin();
336          it != m_driverHandlers.end(); ++it)
337       it->handler->OnAxisMotion(axisIndex, center, center, range);
338     return true;
339   }
340 
341   // Process promiscuous handlers
342   for (auto& it : m_driverHandlers)
343   {
344     if (it.bPromiscuous)
345       it.handler->OnAxisMotion(axisIndex, position, center, range);
346   }
347 
348   bool bHandled = false;
349 
350   // Process regular handlers until one is handled
351   for (auto& it : m_driverHandlers)
352   {
353     if (!it.bPromiscuous)
354     {
355       bHandled |= it.handler->OnAxisMotion(axisIndex, position, center, range);
356 
357       // If axis is centered, force bHandled to false to notify all handlers.
358       // This avoids "sticking".
359       if (position == static_cast<float>(center))
360         bHandled = false;
361 
362       // Once an axis is handled, we're done
363       if (bHandled)
364         break;
365     }
366   }
367 
368   if (bHandled)
369     m_lastActive = CDateTime::GetCurrentDateTime();
370 
371   return bHandled;
372 }
373 
ProcessAxisMotions(void)374 void CPeripheralJoystick::ProcessAxisMotions(void)
375 {
376   CSingleLock lock(m_handlerMutex);
377 
378   for (auto& it : m_driverHandlers)
379     it.handler->ProcessAxisMotions();
380 }
381 
SetMotorState(unsigned int motorIndex,float magnitude)382 bool CPeripheralJoystick::SetMotorState(unsigned int motorIndex, float magnitude)
383 {
384   bool bHandled = false;
385 
386   if (m_mappedBusType == PERIPHERAL_BUS_ADDON)
387   {
388     CPeripheralBusAddon* addonBus = static_cast<CPeripheralBusAddon*>(m_bus);
389     if (addonBus)
390     {
391       bHandled = addonBus->SendRumbleEvent(m_strLocation, motorIndex, magnitude);
392     }
393   }
394   return bHandled;
395 }
396 
SetMotorCount(unsigned int motorCount)397 void CPeripheralJoystick::SetMotorCount(unsigned int motorCount)
398 {
399   m_motorCount = motorCount;
400 
401   if (m_motorCount == 0)
402     m_features.erase(std::remove(m_features.begin(), m_features.end(), FEATURE_RUMBLE),
403                      m_features.end());
404   else if (std::find(m_features.begin(), m_features.end(), FEATURE_RUMBLE) == m_features.end())
405     m_features.push_back(FEATURE_RUMBLE);
406 }
407 
SetSupportsPowerOff(bool bSupportsPowerOff)408 void CPeripheralJoystick::SetSupportsPowerOff(bool bSupportsPowerOff)
409 {
410   m_supportsPowerOff = bSupportsPowerOff;
411 
412   if (!m_supportsPowerOff)
413     m_features.erase(std::remove(m_features.begin(), m_features.end(), FEATURE_POWER_OFF),
414                      m_features.end());
415   else if (std::find(m_features.begin(), m_features.end(), FEATURE_POWER_OFF) == m_features.end())
416     m_features.push_back(FEATURE_POWER_OFF);
417 }
418