1 /*
2  *  Copyright (C) 2016-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 "RumbleGenerator.h"
10 
11 #include "ServiceBroker.h"
12 #include "games/controllers/Controller.h"
13 #include "games/controllers/ControllerIDs.h"
14 #include "games/controllers/ControllerManager.h"
15 #include "input/joysticks/interfaces/IInputReceiver.h"
16 
17 #include <algorithm>
18 
19 #define RUMBLE_TEST_DURATION_MS 1000 // Per motor
20 #define RUMBLE_NOTIFICATION_DURATION_MS 300
21 
22 // From game.controller.default profile
23 #define WEAK_MOTOR_NAME "rightmotor"
24 
25 using namespace KODI;
26 using namespace JOYSTICK;
27 
CRumbleGenerator()28 CRumbleGenerator::CRumbleGenerator()
29   : CThread("RumbleGenerator"), m_motors(GetMotors(ControllerID()))
30 {
31 }
32 
ControllerID() const33 std::string CRumbleGenerator::ControllerID() const
34 {
35   return DEFAULT_CONTROLLER_ID;
36 }
37 
NotifyUser(IInputReceiver * receiver)38 void CRumbleGenerator::NotifyUser(IInputReceiver* receiver)
39 {
40   if (receiver && !m_motors.empty())
41   {
42     if (IsRunning())
43       StopThread(true);
44 
45     m_receiver = receiver;
46     m_type = RUMBLE_NOTIFICATION;
47     Create();
48   }
49 }
50 
DoTest(IInputReceiver * receiver)51 bool CRumbleGenerator::DoTest(IInputReceiver* receiver)
52 {
53   if (receiver && !m_motors.empty())
54   {
55     if (IsRunning())
56       StopThread(true);
57 
58     m_receiver = receiver;
59     m_type = RUMBLE_TEST;
60     Create();
61 
62     return true;
63   }
64   return false;
65 }
66 
Process(void)67 void CRumbleGenerator::Process(void)
68 {
69   switch (m_type)
70   {
71     case RUMBLE_NOTIFICATION:
72     {
73       std::vector<std::string> motors;
74 
75       if (std::find(m_motors.begin(), m_motors.end(), WEAK_MOTOR_NAME) != m_motors.end())
76         motors.emplace_back(WEAK_MOTOR_NAME);
77       else
78         motors = m_motors; // Not using default profile? Just rumble all motors
79 
80       for (const std::string& motor : motors)
81         m_receiver->SetRumbleState(motor, 1.0f);
82 
83       CThread::Sleep(RUMBLE_NOTIFICATION_DURATION_MS);
84 
85       if (m_bStop)
86         break;
87 
88       for (const std::string& motor : motors)
89         m_receiver->SetRumbleState(motor, 0.0f);
90 
91       break;
92     }
93     case RUMBLE_TEST:
94     {
95       for (const std::string& motor : m_motors)
96       {
97         m_receiver->SetRumbleState(motor, 1.0f);
98 
99         CThread::Sleep(RUMBLE_TEST_DURATION_MS);
100 
101         if (m_bStop)
102           break;
103 
104         m_receiver->SetRumbleState(motor, 0.0f);
105       }
106       break;
107     }
108     default:
109       break;
110   }
111 }
112 
GetMotors(const std::string & controllerId)113 std::vector<std::string> CRumbleGenerator::GetMotors(const std::string& controllerId)
114 {
115   using namespace GAME;
116 
117   std::vector<std::string> motors;
118 
119   CControllerManager& controllerManager = CServiceBroker::GetGameControllerManager();
120   ControllerPtr controller = controllerManager.GetController(controllerId);
121   if (controller)
122     controller->GetFeatures(motors, FEATURE_TYPE::MOTOR);
123 
124   return motors;
125 }
126