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