1 /*
2  *  Copyright (C) 2017-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 "GameClientInput.h"
10 
11 #include "GameClientController.h"
12 #include "GameClientHardware.h"
13 #include "GameClientJoystick.h"
14 #include "GameClientKeyboard.h"
15 #include "GameClientMouse.h"
16 #include "GameClientPort.h"
17 #include "GameClientTopology.h"
18 #include "ServiceBroker.h"
19 #include "addons/kodi-dev-kit/include/kodi/addon-instance/Game.h"
20 #include "games/GameServices.h"
21 #include "games/addons/GameClient.h"
22 #include "games/addons/GameClientCallbacks.h"
23 #include "games/controllers/Controller.h"
24 #include "games/controllers/ControllerTopology.h"
25 #include "input/joysticks/JoystickTypes.h"
26 #include "peripherals/EventLockHandle.h"
27 #include "peripherals/Peripherals.h"
28 #include "threads/SingleLock.h"
29 #include "utils/log.h"
30 
31 #include <algorithm>
32 
33 using namespace KODI;
34 using namespace GAME;
35 
CGameClientInput(CGameClient & gameClient,AddonInstance_Game & addonStruct,CCriticalSection & clientAccess)36 CGameClientInput::CGameClientInput(CGameClient& gameClient,
37                                    AddonInstance_Game& addonStruct,
38                                    CCriticalSection& clientAccess)
39   : CGameClientSubsystem(gameClient, addonStruct, clientAccess), m_topology(new CGameClientTopology)
40 {
41 }
42 
~CGameClientInput()43 CGameClientInput::~CGameClientInput()
44 {
45   Deinitialize();
46 }
47 
Initialize()48 void CGameClientInput::Initialize()
49 {
50   LoadTopology();
51 
52   ActivateControllers(m_topology->ControllerTree());
53 
54   SetControllerLayouts(m_topology->ControllerTree().GetControllers());
55 }
56 
Start(IGameInputCallback * input)57 void CGameClientInput::Start(IGameInputCallback* input)
58 {
59   m_inputCallback = input;
60 
61   const CControllerTree& controllers = m_topology->ControllerTree();
62 
63   // Open keyboard
64   //! @todo Move to player manager
65   if (SupportsKeyboard())
66   {
67     auto it = std::find_if(
68         controllers.Ports().begin(), controllers.Ports().end(),
69         [](const CControllerPortNode& port) { return port.PortType() == PORT_TYPE::KEYBOARD; });
70 
71     OpenKeyboard(it->ActiveController().Controller());
72   }
73 
74   // Open mouse
75   //! @todo Move to player manager
76   if (SupportsMouse())
77   {
78     auto it = std::find_if(
79         controllers.Ports().begin(), controllers.Ports().end(),
80         [](const CControllerPortNode& port) { return port.PortType() == PORT_TYPE::MOUSE; });
81 
82     OpenMouse(it->ActiveController().Controller());
83   }
84 
85   // Open joysticks
86   //! @todo Move to player manager
87   for (const auto& port : controllers.Ports())
88   {
89     if (port.PortType() == PORT_TYPE::CONTROLLER && !port.CompatibleControllers().empty())
90     {
91       ControllerPtr controller = port.ActiveController().Controller();
92       OpenJoystick(port.Address(), controller);
93     }
94   }
95 
96   // Ensure hardware is open to receive events
97   m_hardware.reset(new CGameClientHardware(m_gameClient));
98 
99   if (CServiceBroker::IsServiceManagerUp())
100     CServiceBroker::GetPeripherals().RegisterObserver(this);
101 }
102 
Deinitialize()103 void CGameClientInput::Deinitialize()
104 {
105   Stop();
106 
107   m_topology->Clear();
108   m_controllerLayouts.clear();
109 }
110 
Stop()111 void CGameClientInput::Stop()
112 {
113   if (CServiceBroker::IsServiceManagerUp())
114     CServiceBroker::GetPeripherals().UnregisterObserver(this);
115 
116   m_hardware.reset();
117 
118   std::vector<std::string> ports;
119   for (const auto& it : m_joysticks)
120     ports.emplace_back(it.first);
121 
122   for (const std::string& port : ports)
123     CloseJoystick(port);
124   m_portMap.clear();
125 
126   CloseMouse();
127 
128   CloseKeyboard();
129 
130   m_inputCallback = nullptr;
131 }
132 
HasFeature(const std::string & controllerId,const std::string & featureName) const133 bool CGameClientInput::HasFeature(const std::string& controllerId,
134                                   const std::string& featureName) const
135 {
136   bool bHasFeature = false;
137 
138   try
139   {
140     bHasFeature =
141         m_struct.toAddon->HasFeature(&m_struct, controllerId.c_str(), featureName.c_str());
142   }
143   catch (...)
144   {
145     CLog::Log(LOGERROR, "GAME: %s: exception caught in HasFeature()", m_gameClient.ID().c_str());
146 
147     // Fail gracefully
148     bHasFeature = true;
149   }
150 
151   return bHasFeature;
152 }
153 
AcceptsInput() const154 bool CGameClientInput::AcceptsInput() const
155 {
156   if (m_inputCallback != nullptr)
157     return m_inputCallback->AcceptsInput();
158 
159   return false;
160 }
161 
InputEvent(const game_input_event & event)162 bool CGameClientInput::InputEvent(const game_input_event& event)
163 {
164   bool bHandled = false;
165 
166   try
167   {
168     bHandled = m_struct.toAddon->InputEvent(&m_struct, &event);
169   }
170   catch (...)
171   {
172     CLog::Log(LOGERROR, "GAME: %s: exception caught in InputEvent()", m_gameClient.ID().c_str());
173   }
174 
175   return bHandled;
176 }
177 
LoadTopology()178 void CGameClientInput::LoadTopology()
179 {
180   game_input_topology* topologyStruct = nullptr;
181 
182   if (m_gameClient.Initialized())
183   {
184     try
185     {
186       topologyStruct = m_struct.toAddon->GetTopology(&m_struct);
187     }
188     catch (...)
189     {
190       m_gameClient.LogException("GetTopology()");
191     }
192   }
193 
194   GameClientPortVec hardwarePorts;
195   int playerLimit = -1;
196 
197   if (topologyStruct != nullptr)
198   {
199     //! @todo Guard against infinite loops provided by the game client
200 
201     game_input_port* ports = topologyStruct->ports;
202     if (ports != nullptr)
203     {
204       for (unsigned int i = 0; i < topologyStruct->port_count; i++)
205         hardwarePorts.emplace_back(new CGameClientPort(ports[i]));
206     }
207 
208     playerLimit = topologyStruct->player_limit;
209 
210     try
211     {
212       m_struct.toAddon->FreeTopology(&m_struct, topologyStruct);
213     }
214     catch (...)
215     {
216       m_gameClient.LogException("FreeTopology()");
217     }
218   }
219 
220   // If no topology is available, create a default one with a single port that
221   // accepts all controllers imported by addon.xml
222   if (hardwarePorts.empty())
223     hardwarePorts.emplace_back(new CGameClientPort(GetControllers(m_gameClient)));
224 
225   m_topology.reset(new CGameClientTopology(std::move(hardwarePorts), playerLimit));
226 }
227 
ActivateControllers(CControllerHub & hub)228 void CGameClientInput::ActivateControllers(CControllerHub& hub)
229 {
230   for (auto& port : hub.Ports())
231   {
232     port.SetConnected(true);
233     port.SetActiveController(0);
234     ActivateControllers(port.ActiveController().Hub());
235   }
236 }
237 
SetControllerLayouts(const ControllerVector & controllers)238 void CGameClientInput::SetControllerLayouts(const ControllerVector& controllers)
239 {
240   if (controllers.empty())
241     return;
242 
243   for (const auto& controller : controllers)
244   {
245     const std::string controllerId = controller->ID();
246     if (m_controllerLayouts.find(controllerId) == m_controllerLayouts.end())
247       m_controllerLayouts[controllerId].reset(new CGameClientController(*this, controller));
248   }
249 
250   std::vector<game_controller_layout> controllerStructs;
251   for (const auto& it : m_controllerLayouts)
252     controllerStructs.emplace_back(it.second->TranslateController());
253 
254   try
255   {
256     m_struct.toAddon->SetControllerLayouts(&m_struct, controllerStructs.data(),
257                                            static_cast<unsigned int>(controllerStructs.size()));
258   }
259   catch (...)
260   {
261     m_gameClient.LogException("SetControllerLayouts()");
262   }
263 }
264 
GetControllerTree() const265 const CControllerTree& CGameClientInput::GetControllerTree() const
266 {
267   return m_topology->ControllerTree();
268 }
269 
SupportsKeyboard() const270 bool CGameClientInput::SupportsKeyboard() const
271 {
272   const CControllerTree& controllers = m_topology->ControllerTree();
273 
274   auto it = std::find_if(
275       controllers.Ports().begin(), controllers.Ports().end(),
276       [](const CControllerPortNode& port) { return port.PortType() == PORT_TYPE::KEYBOARD; });
277 
278   return it != controllers.Ports().end() && !it->CompatibleControllers().empty();
279 }
280 
SupportsMouse() const281 bool CGameClientInput::SupportsMouse() const
282 {
283   const CControllerTree& controllers = m_topology->ControllerTree();
284 
285   auto it = std::find_if(
286       controllers.Ports().begin(), controllers.Ports().end(),
287       [](const CControllerPortNode& port) { return port.PortType() == PORT_TYPE::MOUSE; });
288 
289   return it != controllers.Ports().end() && !it->CompatibleControllers().empty();
290 }
291 
HasAgent() const292 bool CGameClientInput::HasAgent() const
293 {
294   //! @todo We check m_portMap instead of m_joysticks because m_joysticks is
295   //        always populated with the default joystick configuration (i.e.
296   //        all ports are connected to the first controller they accept).
297   //        The game has no way of knowing which joysticks are actually being
298   //        controlled by agents -- this information is stored in m_portMap,
299   //        which is not exposed to the game.
300   if (!m_portMap.empty())
301     return true;
302 
303   if (m_keyboard)
304     return true;
305 
306   if (m_mouse)
307     return true;
308 
309   return false;
310 }
311 
OpenKeyboard(const ControllerPtr & controller)312 bool CGameClientInput::OpenKeyboard(const ControllerPtr& controller)
313 {
314   using namespace JOYSTICK;
315 
316   if (!controller)
317   {
318     CLog::Log(LOGERROR, "Failed to open keyboard, no controller given");
319     return false;
320   }
321 
322   //! @todo Move to player manager
323   PERIPHERALS::PeripheralVector keyboards;
324   CServiceBroker::GetPeripherals().GetPeripheralsWithFeature(keyboards,
325                                                              PERIPHERALS::FEATURE_KEYBOARD);
326   if (keyboards.empty())
327     return false;
328 
329   bool bSuccess = false;
330 
331   {
332     CSingleLock lock(m_clientAccess);
333 
334     if (m_gameClient.Initialized())
335     {
336       try
337       {
338         bSuccess = m_struct.toAddon->EnableKeyboard(&m_struct, true, controller->ID().c_str());
339       }
340       catch (...)
341       {
342         m_gameClient.LogException("EnableKeyboard()");
343       }
344     }
345   }
346 
347   if (bSuccess)
348   {
349     m_keyboard.reset(
350         new CGameClientKeyboard(m_gameClient, controller->ID(), keyboards.at(0).get()));
351     return true;
352   }
353 
354   return false;
355 }
356 
CloseKeyboard()357 void CGameClientInput::CloseKeyboard()
358 {
359   m_keyboard.reset();
360 
361   {
362     CSingleLock lock(m_clientAccess);
363 
364     if (m_gameClient.Initialized())
365     {
366       try
367       {
368         m_struct.toAddon->EnableKeyboard(&m_struct, false, "");
369       }
370       catch (...)
371       {
372         m_gameClient.LogException("EnableKeyboard()");
373       }
374     }
375   }
376 }
377 
OpenMouse(const ControllerPtr & controller)378 bool CGameClientInput::OpenMouse(const ControllerPtr& controller)
379 {
380   using namespace JOYSTICK;
381 
382   if (!controller)
383   {
384     CLog::Log(LOGERROR, "Failed to open mouse, no controller given");
385     return false;
386   }
387 
388   //! @todo Move to player manager
389   PERIPHERALS::PeripheralVector mice;
390   CServiceBroker::GetPeripherals().GetPeripheralsWithFeature(mice, PERIPHERALS::FEATURE_MOUSE);
391   if (mice.empty())
392     return false;
393 
394   bool bSuccess = false;
395 
396   {
397     CSingleLock lock(m_clientAccess);
398 
399     if (m_gameClient.Initialized())
400     {
401       try
402       {
403         bSuccess = m_struct.toAddon->EnableMouse(&m_struct, true, controller->ID().c_str());
404       }
405       catch (...)
406       {
407         m_gameClient.LogException("EnableMouse()");
408       }
409     }
410   }
411 
412   if (bSuccess)
413   {
414     m_mouse.reset(new CGameClientMouse(m_gameClient, controller->ID(), mice.at(0).get()));
415     return true;
416   }
417 
418   return false;
419 }
420 
CloseMouse()421 void CGameClientInput::CloseMouse()
422 {
423   m_mouse.reset();
424 
425   {
426     CSingleLock lock(m_clientAccess);
427 
428     if (m_gameClient.Initialized())
429     {
430       try
431       {
432         m_struct.toAddon->EnableMouse(&m_struct, false, "");
433       }
434       catch (...)
435       {
436         m_gameClient.LogException("EnableMouse()");
437       }
438     }
439   }
440 }
441 
OpenJoystick(const std::string & portAddress,const ControllerPtr & controller)442 bool CGameClientInput::OpenJoystick(const std::string& portAddress, const ControllerPtr& controller)
443 {
444   using namespace JOYSTICK;
445 
446   if (!controller)
447   {
448     CLog::Log(LOGERROR, "Failed to open port \"%s\", no controller given", portAddress.c_str());
449     return false;
450   }
451 
452   const CControllerTree& controllerTree = m_topology->ControllerTree();
453 
454   const CControllerPortNode& port = controllerTree.GetPort(portAddress);
455   if (!port.IsControllerAccepted(portAddress, controller->ID()))
456   {
457     CLog::Log(LOGERROR, "Failed to open port: Invalid controller \"%s\" on port \"%s\"",
458               controller->ID().c_str(), portAddress.c_str());
459     return false;
460   }
461 
462   bool bSuccess = false;
463 
464   {
465     CSingleLock lock(m_clientAccess);
466 
467     if (m_gameClient.Initialized())
468     {
469       try
470       {
471         bSuccess = m_struct.toAddon->ConnectController(&m_struct, true, portAddress.c_str(),
472                                                        controller->ID().c_str());
473       }
474       catch (...)
475       {
476         m_gameClient.LogException("ConnectController()");
477       }
478     }
479   }
480 
481   if (bSuccess)
482   {
483     PERIPHERALS::EventLockHandlePtr lock = CServiceBroker::GetPeripherals().RegisterEventLock();
484 
485     m_joysticks[portAddress].reset(new CGameClientJoystick(m_gameClient, portAddress, controller));
486     ProcessJoysticks();
487 
488     return true;
489   }
490 
491   return false;
492 }
493 
CloseJoystick(const std::string & portAddress)494 void CGameClientInput::CloseJoystick(const std::string& portAddress)
495 {
496   auto it = m_joysticks.find(portAddress);
497   if (it != m_joysticks.end())
498   {
499     std::unique_ptr<CGameClientJoystick> joystick = std::move(it->second);
500     m_joysticks.erase(it);
501     {
502       PERIPHERALS::EventLockHandlePtr lock = CServiceBroker::GetPeripherals().RegisterEventLock();
503 
504       ProcessJoysticks();
505       joystick.reset();
506     }
507   }
508 
509   {
510     CSingleLock lock(m_clientAccess);
511 
512     if (m_gameClient.Initialized())
513     {
514       try
515       {
516         m_struct.toAddon->ConnectController(&m_struct, false, portAddress.c_str(), "");
517       }
518       catch (...)
519       {
520         m_gameClient.LogException("ConnectController()");
521       }
522     }
523   }
524 }
525 
HardwareReset()526 void CGameClientInput::HardwareReset()
527 {
528   if (m_hardware)
529     m_hardware->OnResetButton();
530 }
531 
ReceiveInputEvent(const game_input_event & event)532 bool CGameClientInput::ReceiveInputEvent(const game_input_event& event)
533 {
534   bool bHandled = false;
535 
536   switch (event.type)
537   {
538     case GAME_INPUT_EVENT_MOTOR:
539       if (event.port_address != nullptr && event.feature_name != nullptr)
540         bHandled = SetRumble(event.port_address, event.feature_name, event.motor.magnitude);
541       break;
542     default:
543       break;
544   }
545 
546   return bHandled;
547 }
548 
SetRumble(const std::string & portAddress,const std::string & feature,float magnitude)549 bool CGameClientInput::SetRumble(const std::string& portAddress,
550                                  const std::string& feature,
551                                  float magnitude)
552 {
553   bool bHandled = false;
554 
555   auto it = m_joysticks.find(portAddress);
556   if (it != m_joysticks.end())
557     bHandled = it->second->SetRumble(feature, magnitude);
558 
559   return bHandled;
560 }
561 
Notify(const Observable & obs,const ObservableMessage msg)562 void CGameClientInput::Notify(const Observable& obs, const ObservableMessage msg)
563 {
564   switch (msg)
565   {
566     case ObservableMessagePeripheralsChanged:
567     {
568       PERIPHERALS::EventLockHandlePtr lock = CServiceBroker::GetPeripherals().RegisterEventLock();
569 
570       ProcessJoysticks();
571 
572       break;
573     }
574     default:
575       break;
576   }
577 }
578 
ProcessJoysticks()579 void CGameClientInput::ProcessJoysticks()
580 {
581   PERIPHERALS::PeripheralVector joysticks;
582   CServiceBroker::GetPeripherals().GetPeripheralsWithFeature(joysticks,
583                                                              PERIPHERALS::FEATURE_JOYSTICK);
584 
585   // Update expired joysticks
586   PortMap portMapCopy = m_portMap;
587   for (auto& it : portMapCopy)
588   {
589     JOYSTICK::IInputProvider* inputProvider = it.first;
590     CGameClientJoystick* gameJoystick = it.second;
591 
592     const bool bExpired =
593         std::find_if(joysticks.begin(), joysticks.end(),
594                      [inputProvider](const PERIPHERALS::PeripheralPtr& joystick) {
595                        return inputProvider ==
596                               static_cast<JOYSTICK::IInputProvider*>(joystick.get());
597                      }) == joysticks.end();
598 
599     if (bExpired)
600     {
601       gameJoystick->UnregisterInput(nullptr);
602       m_portMap.erase(inputProvider);
603     }
604   }
605 
606   // Perform the port mapping
607   PortMap newPortMap = MapJoysticks(joysticks, m_joysticks);
608 
609   // Update connected joysticks
610   for (auto& peripheralJoystick : joysticks)
611   {
612     // Upcast to input interface
613     JOYSTICK::IInputProvider* inputProvider = peripheralJoystick.get();
614 
615     auto itConnectedPort = newPortMap.find(inputProvider);
616     auto itDisconnectedPort = m_portMap.find(inputProvider);
617 
618     CGameClientJoystick* newJoystick =
619         itConnectedPort != newPortMap.end() ? itConnectedPort->second : nullptr;
620     CGameClientJoystick* oldJoystick =
621         itDisconnectedPort != m_portMap.end() ? itDisconnectedPort->second : nullptr;
622 
623     if (oldJoystick != newJoystick)
624     {
625       // Unregister old input handler
626       if (oldJoystick != nullptr)
627       {
628         oldJoystick->UnregisterInput(inputProvider);
629         m_portMap.erase(itDisconnectedPort);
630       }
631 
632       // Register new handler
633       if (newJoystick != nullptr)
634       {
635         newJoystick->RegisterInput(inputProvider);
636         m_portMap[inputProvider] = newJoystick;
637       }
638     }
639   }
640 }
641 
MapJoysticks(const PERIPHERALS::PeripheralVector & peripheralJoysticks,const JoystickMap & gameClientjoysticks) const642 CGameClientInput::PortMap CGameClientInput::MapJoysticks(
643     const PERIPHERALS::PeripheralVector& peripheralJoysticks,
644     const JoystickMap& gameClientjoysticks) const
645 {
646   PortMap result;
647 
648   //! @todo Preserve existing joystick ports
649 
650   // Sort by order of last button press
651   PERIPHERALS::PeripheralVector sortedJoysticks = peripheralJoysticks;
652   std::sort(sortedJoysticks.begin(), sortedJoysticks.end(),
653             [](const PERIPHERALS::PeripheralPtr& lhs, const PERIPHERALS::PeripheralPtr& rhs) {
654               if (lhs->LastActive().IsValid() && !rhs->LastActive().IsValid())
655                 return true;
656               if (!lhs->LastActive().IsValid() && rhs->LastActive().IsValid())
657                 return false;
658 
659               return lhs->LastActive() > rhs->LastActive();
660             });
661 
662   unsigned int i = 0;
663   for (const auto& it : gameClientjoysticks)
664   {
665     if (i >= peripheralJoysticks.size())
666       break;
667 
668     // Check topology player limit
669     const int playerLimit = m_topology->PlayerLimit();
670     if (playerLimit >= 0 && static_cast<int>(i) >= playerLimit)
671       break;
672 
673     // Dereference iterators
674     const PERIPHERALS::PeripheralPtr& peripheralJoystick = sortedJoysticks[i++];
675     const std::unique_ptr<CGameClientJoystick>& gameClientJoystick = it.second;
676 
677     // Map input provider to input handler
678     result[peripheralJoystick.get()] = gameClientJoystick.get();
679   }
680 
681   return result;
682 }
683 
GetControllers(const CGameClient & gameClient)684 ControllerVector CGameClientInput::GetControllers(const CGameClient& gameClient)
685 {
686   using namespace ADDON;
687 
688   ControllerVector controllers;
689 
690   CGameServices& gameServices = CServiceBroker::GetGameServices();
691 
692   const auto& dependencies = gameClient.GetDependencies();
693   for (auto it = dependencies.begin(); it != dependencies.end(); ++it)
694   {
695     ControllerPtr controller = gameServices.GetController(it->id);
696     if (controller)
697       controllers.push_back(controller);
698   }
699 
700   if (controllers.empty())
701   {
702     // Use the default controller
703     ControllerPtr controller = gameServices.GetDefaultController();
704     if (controller)
705       controllers.push_back(controller);
706   }
707 
708   return controllers;
709 }
710