1 /*
2  *  Copyright (C) 2005-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 "InputManager.h"
10 
11 #include "AppInboundProtocol.h"
12 #include "AppParamParser.h"
13 #include "Application.h"
14 #include "ButtonTranslator.h"
15 #include "CustomControllerTranslator.h"
16 #include "IRTranslator.h"
17 #include "JoystickMapper.h"
18 #include "KeymapEnvironment.h"
19 #include "ServiceBroker.h"
20 #include "TouchTranslator.h"
21 #include "Util.h"
22 #include "XBMC_vkeys.h"
23 #include "guilib/GUIAudioManager.h"
24 #include "guilib/GUIComponent.h"
25 #include "guilib/GUIControl.h"
26 #include "guilib/GUIMessage.h"
27 #include "guilib/GUIWindow.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "input/Key.h"
30 #include "input/keyboard/KeyboardEasterEgg.h"
31 #include "input/keyboard/interfaces/IKeyboardDriverHandler.h"
32 #include "input/mouse/MouseTranslator.h"
33 #include "input/mouse/interfaces/IMouseDriverHandler.h"
34 #include "messaging/ApplicationMessenger.h"
35 #include "network/EventServer.h"
36 #include "peripherals/Peripherals.h"
37 #include "settings/Settings.h"
38 #include "settings/SettingsComponent.h"
39 #include "settings/lib/Setting.h"
40 #include "utils/Geometry.h"
41 #include "utils/StringUtils.h"
42 #include "utils/log.h"
43 
44 #include <algorithm>
45 #include <math.h>
46 
47 using EVENTSERVER::CEventServer;
48 
49 using namespace KODI;
50 using namespace MESSAGING;
51 
52 const std::string CInputManager::SETTING_INPUT_ENABLE_CONTROLLER = "input.enablejoystick";
53 
CInputManager(const CAppParamParser & params)54 CInputManager::CInputManager(const CAppParamParser& params)
55   : m_keymapEnvironment(new CKeymapEnvironment),
56     m_buttonTranslator(new CButtonTranslator),
57     m_customControllerTranslator(new CCustomControllerTranslator),
58     m_touchTranslator(new CTouchTranslator),
59     m_joystickTranslator(new CJoystickMapper),
60     m_keyboardEasterEgg(new KEYBOARD::CKeyboardEasterEgg)
61 {
62   m_buttonTranslator->RegisterMapper("touch", m_touchTranslator.get());
63   m_buttonTranslator->RegisterMapper("customcontroller", m_customControllerTranslator.get());
64   m_buttonTranslator->RegisterMapper("joystick", m_joystickTranslator.get());
65 
66   RegisterKeyboardDriverHandler(m_keyboardEasterEgg.get());
67 
68   // Register settings
69   std::set<std::string> settingSet;
70   settingSet.insert(CSettings::SETTING_INPUT_ENABLEMOUSE);
71   settingSet.insert(SETTING_INPUT_ENABLE_CONTROLLER);
72   CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, settingSet);
73 }
74 
~CInputManager()75 CInputManager::~CInputManager()
76 {
77   Deinitialize();
78 
79   // Unregister settings
80   CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
81 
82   UnregisterKeyboardDriverHandler(m_keyboardEasterEgg.get());
83 
84   m_buttonTranslator->UnregisterMapper(m_touchTranslator.get());
85   m_buttonTranslator->UnregisterMapper(m_customControllerTranslator.get());
86   m_buttonTranslator->UnregisterMapper(m_joystickTranslator.get());
87 }
88 
InitializeInputs()89 void CInputManager::InitializeInputs()
90 {
91   m_Keyboard.Initialize();
92 
93   m_Mouse.Initialize();
94   m_Mouse.SetEnabled(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
95       CSettings::SETTING_INPUT_ENABLEMOUSE));
96 
97   m_enableController = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
98       SETTING_INPUT_ENABLE_CONTROLLER);
99 }
100 
Deinitialize()101 void CInputManager::Deinitialize()
102 {
103 }
104 
ProcessPeripherals(float frameTime)105 bool CInputManager::ProcessPeripherals(float frameTime)
106 {
107   CKey key;
108   if (CServiceBroker::GetPeripherals().GetNextKeypress(frameTime, key))
109     return OnKey(key);
110   return false;
111 }
112 
ProcessMouse(int windowId)113 bool CInputManager::ProcessMouse(int windowId)
114 {
115   if (!m_Mouse.IsActive() || !g_application.IsAppFocused())
116     return false;
117 
118   // Get the mouse command ID
119   uint32_t mousekey = m_Mouse.GetKey();
120   if (mousekey == KEY_MOUSE_NOOP)
121     return true;
122 
123   // Reset the screensaver and idle timers
124   g_application.ResetSystemIdleTimer();
125   g_application.ResetScreenSaver();
126 
127   if (g_application.WakeUpScreenSaverAndDPMS())
128     return true;
129 
130   // Retrieve the corresponding action
131   CKey key(mousekey, (unsigned int)0);
132   CAction mouseaction = m_buttonTranslator->GetAction(windowId, key);
133 
134   // Deactivate mouse if non-mouse action
135   if (!mouseaction.IsMouse())
136     m_Mouse.SetActive(false);
137 
138   // Consume ACTION_NOOP.
139   // Some views or dialogs gets closed after any ACTION and
140   // a sensitive mouse might cause problems.
141   if (mouseaction.GetID() == ACTION_NOOP)
142     return false;
143 
144   // If we couldn't find an action return false to indicate we have not
145   // handled this mouse action
146   if (!mouseaction.GetID())
147   {
148     CLog::LogF(LOGDEBUG, "unknown mouse command %d", mousekey);
149     return false;
150   }
151 
152   // Log mouse actions except for move and noop
153   if (mouseaction.GetID() != ACTION_MOUSE_MOVE && mouseaction.GetID() != ACTION_NOOP)
154     CLog::LogF(LOGDEBUG, "trying mouse action %s", mouseaction.GetName().c_str());
155 
156   // The action might not be a mouse action. For example wheel moves might
157   // be mapped to volume up/down in mouse.xml. In this case we do not want
158   // the mouse position saved in the action.
159   if (!mouseaction.IsMouse())
160     return g_application.OnAction(mouseaction);
161 
162   // This is a mouse action so we need to record the mouse position
163   return g_application.OnAction(CAction(mouseaction.GetID(), m_Mouse.GetHold(MOUSE_LEFT_BUTTON),
164                                         (float)m_Mouse.GetX(), (float)m_Mouse.GetY(),
165                                         (float)m_Mouse.GetDX(), (float)m_Mouse.GetDY(), 0.0f, 0.0f,
166                                         mouseaction.GetName()));
167 }
168 
ProcessEventServer(int windowId,float frameTime)169 bool CInputManager::ProcessEventServer(int windowId, float frameTime)
170 {
171   CEventServer* es = CEventServer::GetInstance();
172   if (!es || !es->Running() || es->GetNumberOfClients() == 0)
173     return false;
174 
175   // process any queued up actions
176   if (es->ExecuteNextAction())
177   {
178     // reset idle timers
179     g_application.ResetSystemIdleTimer();
180     g_application.ResetScreenSaver();
181     g_application.WakeUpScreenSaverAndDPMS();
182   }
183 
184   // now handle any buttons or axis
185   std::string strMapName;
186   bool isAxis = false;
187   float fAmount = 0.0;
188   bool isJoystick = false;
189 
190   // es->ExecuteNextAction() invalidates the ref to the CEventServer instance
191   // when the action exits XBMC
192   es = CEventServer::GetInstance();
193   if (!es || !es->Running() || es->GetNumberOfClients() == 0)
194     return false;
195   unsigned int wKeyID = es->GetButtonCode(strMapName, isAxis, fAmount, isJoystick);
196 
197   if (wKeyID)
198   {
199     if (strMapName.length() > 0)
200     {
201       // joysticks are not supported via eventserver
202       if (isJoystick)
203       {
204         return false;
205       }
206       else // it is a customcontroller
207       {
208         int actionID;
209         std::string actionName;
210 
211         // Translate using custom controller translator.
212         if (m_customControllerTranslator->TranslateCustomControllerString(
213                 windowId, strMapName, wKeyID, actionID, actionName))
214         {
215           // break screensaver
216           g_application.ResetSystemIdleTimer();
217           g_application.ResetScreenSaver();
218 
219           // in case we wokeup the screensaver or screen - eat that action...
220           if (g_application.WakeUpScreenSaverAndDPMS())
221             return true;
222 
223           m_Mouse.SetActive(false);
224 
225           CLog::Log(LOGDEBUG, "EventServer: key %d translated to action %s", wKeyID, actionName);
226 
227           return ExecuteInputAction(CAction(actionID, fAmount, 0.0f, actionName));
228         }
229         else
230         {
231           CLog::Log(LOGDEBUG, "ERROR mapping customcontroller action. CustomController: %s %i",
232                     strMapName.c_str(), wKeyID);
233         }
234       }
235     }
236     else
237     {
238       CKey key;
239       if (wKeyID & ES_FLAG_UNICODE)
240       {
241         key = CKey(0u, 0u, static_cast<wchar_t>(wKeyID & ~ES_FLAG_UNICODE), 0, 0, 0, 0);
242         return OnKey(key);
243       }
244 
245       if (wKeyID == KEY_BUTTON_LEFT_ANALOG_TRIGGER)
246         key = CKey(wKeyID, static_cast<uint8_t>(255 * fAmount), 0, 0.0, 0.0, 0.0, 0.0, frameTime);
247       else if (wKeyID == KEY_BUTTON_RIGHT_ANALOG_TRIGGER)
248         key = CKey(wKeyID, 0, static_cast<uint8_t>(255 * fAmount), 0.0, 0.0, 0.0, 0.0, frameTime);
249       else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_LEFT)
250         key = CKey(wKeyID, 0, 0, -fAmount, 0.0, 0.0, 0.0, frameTime);
251       else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_RIGHT)
252         key = CKey(wKeyID, 0, 0, fAmount, 0.0, 0.0, 0.0, frameTime);
253       else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_UP)
254         key = CKey(wKeyID, 0, 0, 0.0, fAmount, 0.0, 0.0, frameTime);
255       else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_DOWN)
256         key = CKey(wKeyID, 0, 0, 0.0, -fAmount, 0.0, 0.0, frameTime);
257       else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_LEFT)
258         key = CKey(wKeyID, 0, 0, 0.0, 0.0, -fAmount, 0.0, frameTime);
259       else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT)
260         key = CKey(wKeyID, 0, 0, 0.0, 0.0, fAmount, 0.0, frameTime);
261       else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_UP)
262         key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, fAmount, frameTime);
263       else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_DOWN)
264         key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, -fAmount, frameTime);
265       else
266         key = CKey(wKeyID);
267       key.SetFromService(true);
268       return OnKey(key);
269     }
270   }
271 
272   {
273     CPoint pos;
274     if (es->GetMousePos(pos.x, pos.y) && m_Mouse.IsEnabled())
275     {
276       XBMC_Event newEvent;
277       newEvent.type = XBMC_MOUSEMOTION;
278       newEvent.motion.x = (uint16_t)pos.x;
279       newEvent.motion.y = (uint16_t)pos.y;
280       CServiceBroker::GetAppPort()->OnEvent(
281           newEvent); // had to call this to update g_Mouse position
282       return g_application.OnAction(CAction(ACTION_MOUSE_MOVE, pos.x, pos.y));
283     }
284   }
285 
286   return false;
287 }
288 
ProcessQueuedActions()289 void CInputManager::ProcessQueuedActions()
290 {
291   std::vector<CAction> queuedActions;
292   {
293     CSingleLock lock(m_actionMutex);
294     queuedActions.swap(m_queuedActions);
295   }
296 
297   for (const CAction& action : queuedActions)
298     g_application.OnAction(action);
299 }
300 
QueueAction(const CAction & action)301 void CInputManager::QueueAction(const CAction& action)
302 {
303   CSingleLock lock(m_actionMutex);
304 
305   // Avoid dispatching multiple analog actions per frame with the same ID
306   if (action.IsAnalog())
307   {
308     m_queuedActions.erase(std::remove_if(m_queuedActions.begin(), m_queuedActions.end(),
309                                          [&action](const CAction& queuedAction) {
310                                            return action.GetID() == queuedAction.GetID();
311                                          }),
312                           m_queuedActions.end());
313   }
314 
315   m_queuedActions.push_back(action);
316 }
317 
Process(int windowId,float frameTime)318 bool CInputManager::Process(int windowId, float frameTime)
319 {
320   // process input actions
321   ProcessEventServer(windowId, frameTime);
322   ProcessPeripherals(frameTime);
323   ProcessQueuedActions();
324 
325   // Inform the environment of the new active window ID
326   m_keymapEnvironment->SetWindowID(windowId);
327 
328   return true;
329 }
330 
OnEvent(XBMC_Event & newEvent)331 bool CInputManager::OnEvent(XBMC_Event& newEvent)
332 {
333   switch (newEvent.type)
334   {
335     case XBMC_KEYDOWN:
336     {
337       m_Keyboard.ProcessKeyDown(newEvent.key.keysym);
338       CKey key = m_Keyboard.TranslateKey(newEvent.key.keysym);
339       OnKey(key);
340       break;
341     }
342     case XBMC_KEYUP:
343       m_Keyboard.ProcessKeyUp();
344       OnKeyUp(m_Keyboard.TranslateKey(newEvent.key.keysym));
345       break;
346     case XBMC_MOUSEBUTTONDOWN:
347     case XBMC_MOUSEBUTTONUP:
348     case XBMC_MOUSEMOTION:
349     {
350       bool handled = false;
351 
352       for (auto driverHandler : m_mouseHandlers)
353       {
354         switch (newEvent.type)
355         {
356           case XBMC_MOUSEMOTION:
357           {
358             if (driverHandler->OnPosition(newEvent.motion.x, newEvent.motion.y))
359               handled = true;
360             break;
361           }
362           case XBMC_MOUSEBUTTONDOWN:
363           {
364             MOUSE::BUTTON_ID buttonId;
365             if (CMouseTranslator::TranslateEventID(newEvent.button.button, buttonId))
366             {
367               if (driverHandler->OnButtonPress(buttonId))
368                 handled = true;
369             }
370             break;
371           }
372           case XBMC_MOUSEBUTTONUP:
373           {
374             MOUSE::BUTTON_ID buttonId;
375             if (CMouseTranslator::TranslateEventID(newEvent.button.button, buttonId))
376               driverHandler->OnButtonRelease(buttonId);
377             break;
378           }
379           default:
380             break;
381         }
382 
383         if (handled)
384           break;
385       }
386 
387       if (!handled)
388       {
389         m_Mouse.HandleEvent(newEvent);
390         ProcessMouse(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
391       }
392       break;
393     }
394     case XBMC_TOUCH:
395     {
396       if (newEvent.touch.action == ACTION_TOUCH_TAP)
397       { // Send a mouse motion event with no dx,dy for getting the current guiitem selected
398         g_application.OnAction(
399             CAction(ACTION_MOUSE_MOVE, 0, newEvent.touch.x, newEvent.touch.y, 0, 0));
400       }
401       int actionId = 0;
402       std::string actionString;
403       if (newEvent.touch.action == ACTION_GESTURE_BEGIN ||
404           newEvent.touch.action == ACTION_GESTURE_END ||
405           newEvent.touch.action == ACTION_GESTURE_ABORT)
406         actionId = newEvent.touch.action;
407       else
408       {
409         int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
410         m_touchTranslator->TranslateTouchAction(iWin, newEvent.touch.action,
411                                                 newEvent.touch.pointers, actionId, actionString);
412       }
413 
414       if (actionId <= 0)
415         return false;
416 
417       if ((actionId >= ACTION_TOUCH_TAP && actionId <= ACTION_GESTURE_END) ||
418           (actionId >= ACTION_MOUSE_START && actionId <= ACTION_MOUSE_END))
419       {
420         auto action =
421             new CAction(actionId, 0, newEvent.touch.x, newEvent.touch.y, newEvent.touch.x2,
422                         newEvent.touch.y2, newEvent.touch.x3, newEvent.touch.y3);
423         CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
424                                                      static_cast<void*>(action));
425       }
426       else
427       {
428         if (actionId == ACTION_BUILT_IN_FUNCTION && !actionString.empty())
429           CApplicationMessenger::GetInstance().PostMsg(
430               TMSG_GUI_ACTION, WINDOW_INVALID, -1,
431               static_cast<void*>(new CAction(actionId, actionString)));
432         else
433           CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
434                                                        static_cast<void*>(new CAction(actionId)));
435       }
436 
437       break;
438     } // case
439     case XBMC_BUTTON:
440     {
441       HandleKey(
442           m_buttonStat.TranslateKey(CKey(newEvent.keybutton.button, newEvent.keybutton.holdtime)));
443       break;
444     }
445   } // switch
446 
447   return true;
448 }
449 
450 // OnKey() translates the key into a CAction which is sent on to our Window Manager.
451 // The window manager will return true if the event is processed, false otherwise.
452 // If not already processed, this routine handles global keypresses.  It returns
453 // true if the key has been processed, false otherwise.
454 
OnKey(const CKey & key)455 bool CInputManager::OnKey(const CKey& key)
456 {
457   bool bHandled = false;
458 
459   for (auto handler : m_keyboardHandlers)
460   {
461     if (handler->OnKeyPress(key))
462     {
463       bHandled = true;
464       break;
465     }
466   }
467 
468   if (bHandled)
469   {
470     m_LastKey.Reset();
471   }
472   else
473   {
474     if (key.GetButtonCode() == m_LastKey.GetButtonCode() &&
475         (m_LastKey.GetButtonCode() & CKey::MODIFIER_LONG))
476     {
477       // Do not repeat long presses
478     }
479     else
480     {
481       // Event server keyboard doesn't give normal key up and key down, so don't
482       // process for long press if that is the source
483       if (key.GetFromService() ||
484           !m_buttonTranslator->HasLongpressMapping(
485               CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), key))
486       {
487         m_LastKey.Reset();
488         bHandled = HandleKey(key);
489       }
490       else
491       {
492         if (key.GetButtonCode() != m_LastKey.GetButtonCode() &&
493             (key.GetButtonCode() & CKey::MODIFIER_LONG))
494         {
495           m_LastKey = key; // OnKey is reentrant; need to do this before entering
496           bHandled = HandleKey(key);
497         }
498 
499         m_LastKey = key;
500       }
501     }
502   }
503 
504   return bHandled;
505 }
506 
HandleKey(const CKey & key)507 bool CInputManager::HandleKey(const CKey& key)
508 {
509   // Turn the mouse off, as we've just got a keypress from controller or remote
510   m_Mouse.SetActive(false);
511 
512   // get the current active window
513   int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
514 
515   // this will be checked for certain keycodes that need
516   // special handling if the screensaver is active
517   CAction action = m_buttonTranslator->GetAction(iWin, key);
518 
519   // a key has been pressed.
520   // reset Idle Timer
521   g_application.ResetSystemIdleTimer();
522   bool processKey = AlwaysProcess(action);
523 
524   if (StringUtils::StartsWithNoCase(action.GetName(), "CECToggleState") ||
525       StringUtils::StartsWithNoCase(action.GetName(), "CECStandby"))
526   {
527     // do not wake up the screensaver right after switching off the playing device
528     if (StringUtils::StartsWithNoCase(action.GetName(), "CECToggleState"))
529     {
530       CLog::LogF(LOGDEBUG, "action %s [%d], toggling state of playing device",
531                  action.GetName().c_str(), action.GetID());
532       bool result;
533       CApplicationMessenger::GetInstance().SendMsg(TMSG_CECTOGGLESTATE, 0, 0,
534                                                    static_cast<void*>(&result));
535       if (!result)
536         return true;
537     }
538     else
539     {
540       CApplicationMessenger::GetInstance().PostMsg(TMSG_CECSTANDBY);
541       return true;
542     }
543   }
544 
545   g_application.ResetScreenSaver();
546 
547   // allow some keys to be processed while the screensaver is active
548   if (g_application.WakeUpScreenSaverAndDPMS(processKey) && !processKey)
549   {
550     CLog::LogF(LOGDEBUG, "%s pressed, screen saver/dpms woken up",
551                m_Keyboard.GetKeyName((int)key.GetButtonCode()).c_str());
552     return true;
553   }
554 
555   if (iWin != WINDOW_FULLSCREEN_VIDEO && iWin != WINDOW_FULLSCREEN_GAME)
556   {
557     // current active window isnt the fullscreen window
558     // just use corresponding section from keymap.xml
559     // to map key->action
560 
561     // first determine if we should use keyboard input directly
562     bool useKeyboard =
563         key.FromKeyboard() && (iWin == WINDOW_DIALOG_KEYBOARD || iWin == WINDOW_DIALOG_NUMERIC);
564     CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iWin);
565     if (window)
566     {
567       CGUIControl* control = window->GetFocusedControl();
568       if (control)
569       {
570         // If this is an edit control set usekeyboard to true. This causes the
571         // keypress to be processed directly not through the key mappings.
572         if (control->GetControlType() == CGUIControl::GUICONTROL_EDIT)
573           useKeyboard = true;
574 
575         // If the key pressed is shift-A to shift-Z set usekeyboard to true.
576         // This causes the keypress to be used for list navigation.
577         if (control->IsContainer() && key.GetModifiers() == CKey::MODIFIER_SHIFT &&
578             key.GetUnicode())
579           useKeyboard = true;
580       }
581     }
582     if (useKeyboard)
583     {
584       // use the virtualkeyboard section of the keymap, and send keyboard-specific or navigation
585       // actions through if that's what they are
586       CAction action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key);
587       if (!(action.GetID() == ACTION_MOVE_LEFT || action.GetID() == ACTION_MOVE_RIGHT ||
588             action.GetID() == ACTION_MOVE_UP || action.GetID() == ACTION_MOVE_DOWN ||
589             action.GetID() == ACTION_SELECT_ITEM || action.GetID() == ACTION_ENTER ||
590             action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_NAV_BACK ||
591             action.GetID() == ACTION_VOICE_RECOGNIZE))
592       {
593         // the action isn't plain navigation - check for a keyboard-specific keymap
594         action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key, false);
595         if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) ||
596             action.GetID() == ACTION_BACKSPACE || action.GetID() == ACTION_SHIFT ||
597             action.GetID() == ACTION_SYMBOLS || action.GetID() == ACTION_CURSOR_LEFT ||
598             action.GetID() == ACTION_CURSOR_RIGHT)
599           action = CAction(0); // don't bother with this action
600       }
601       // else pass the keys through directly
602       if (!action.GetID())
603       {
604         if (key.GetFromService())
605           action = CAction(key.GetButtonCode() != KEY_INVALID ? key.GetButtonCode() : 0,
606                            key.GetUnicode());
607         else
608         {
609           // Check for paste keypress
610 #ifdef TARGET_WINDOWS
611           // In Windows paste is ctrl-V
612           if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL)
613 #elif defined(TARGET_LINUX)
614           // In Linux paste is ctrl-V
615           if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL)
616 #elif defined(TARGET_DARWIN_OSX)
617           // In OSX paste is cmd-V
618           if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_META)
619 #else
620           // Placeholder for other operating systems
621           if (false)
622 #endif
623             action = CAction(ACTION_PASTE);
624           // If the unicode is non-zero the keypress is a non-printing character
625           else if (key.GetUnicode())
626             action = CAction(KEY_UNICODE, key.GetUnicode());
627           // The keypress is a non-printing character
628           else
629             action = CAction(key.GetVKey() | KEY_VKEY);
630         }
631       }
632 
633       CLog::LogF(LOGDEBUG, "%s pressed, trying keyboard action %x",
634                  m_Keyboard.GetKeyName((int)key.GetButtonCode()).c_str(), action.GetID());
635 
636       if (g_application.OnAction(action))
637         return true;
638       // failed to handle the keyboard action, drop down through to standard action
639     }
640     if (key.GetFromService())
641     {
642       if (key.GetButtonCode() != KEY_INVALID)
643         action = m_buttonTranslator->GetAction(iWin, key);
644     }
645     else
646       action = m_buttonTranslator->GetAction(iWin, key);
647   }
648   if (!key.IsAnalogButton())
649     CLog::LogF(LOGDEBUG, "%s pressed, action is %s",
650                m_Keyboard.GetKeyName((int)key.GetButtonCode()).c_str(), action.GetName().c_str());
651 
652   return ExecuteInputAction(action);
653 }
654 
OnKeyUp(const CKey & key)655 void CInputManager::OnKeyUp(const CKey& key)
656 {
657   for (auto handler : m_keyboardHandlers)
658     handler->OnKeyRelease(key);
659 
660   if (m_LastKey.GetButtonCode() != KEY_INVALID &&
661       !(m_LastKey.GetButtonCode() & CKey::MODIFIER_LONG))
662   {
663     CKey key = m_LastKey;
664     m_LastKey.Reset(); // OnKey is reentrant; need to do this before entering
665     HandleKey(key);
666   }
667   else
668     m_LastKey.Reset();
669 }
670 
AlwaysProcess(const CAction & action)671 bool CInputManager::AlwaysProcess(const CAction& action)
672 {
673   // check if this button is mapped to a built-in function
674   if (!action.GetName().empty())
675   {
676     std::string builtInFunction;
677     std::vector<std::string> params;
678     CUtil::SplitExecFunction(action.GetName(), builtInFunction, params);
679     StringUtils::ToLower(builtInFunction);
680 
681     // should this button be handled normally or just cancel the screensaver?
682     if (builtInFunction == "powerdown" || builtInFunction == "reboot" ||
683         builtInFunction == "restart" || builtInFunction == "restartapp" ||
684         builtInFunction == "suspend" || builtInFunction == "hibernate" ||
685         builtInFunction == "quit" || builtInFunction == "shutdown")
686     {
687       return true;
688     }
689   }
690 
691   return false;
692 }
693 
ExecuteInputAction(const CAction & action)694 bool CInputManager::ExecuteInputAction(const CAction& action)
695 {
696   bool bResult = false;
697   CGUIComponent* gui = CServiceBroker::GetGUI();
698 
699   // play sound before the action unless the button is held,
700   // where we execute after the action as held actions aren't fired every time.
701   if (action.GetHoldTime())
702   {
703     bResult = g_application.OnAction(action);
704     if (bResult && gui)
705       gui->GetAudioManager().PlayActionSound(action);
706   }
707   else
708   {
709     if (gui)
710       gui->GetAudioManager().PlayActionSound(action);
711 
712     bResult = g_application.OnAction(action);
713   }
714   return bResult;
715 }
716 
HasBuiltin(const std::string & command)717 bool CInputManager::HasBuiltin(const std::string& command)
718 {
719   return false;
720 }
721 
ExecuteBuiltin(const std::string & execute,const std::vector<std::string> & params)722 int CInputManager::ExecuteBuiltin(const std::string& execute,
723                                   const std::vector<std::string>& params)
724 {
725   return 0;
726 }
727 
SetMouseActive(bool active)728 void CInputManager::SetMouseActive(bool active /* = true */)
729 {
730   m_Mouse.SetActive(active);
731 }
732 
SetMouseEnabled(bool mouseEnabled)733 void CInputManager::SetMouseEnabled(bool mouseEnabled /* = true */)
734 {
735   m_Mouse.SetEnabled(mouseEnabled);
736 }
737 
IsMouseActive()738 bool CInputManager::IsMouseActive()
739 {
740   return m_Mouse.IsActive();
741 }
742 
GetMouseState()743 MOUSE_STATE CInputManager::GetMouseState()
744 {
745   return m_Mouse.GetState();
746 }
747 
GetMousePosition()748 MousePosition CInputManager::GetMousePosition()
749 {
750   return m_Mouse.GetPosition();
751 }
752 
SetMouseResolution(int maxX,int maxY,float speedX,float speedY)753 void CInputManager::SetMouseResolution(int maxX, int maxY, float speedX, float speedY)
754 {
755   m_Mouse.SetResolution(maxX, maxY, speedX, speedY);
756 }
757 
SetMouseState(MOUSE_STATE mouseState)758 void CInputManager::SetMouseState(MOUSE_STATE mouseState)
759 {
760   m_Mouse.SetState(mouseState);
761 }
762 
IsControllerEnabled() const763 bool CInputManager::IsControllerEnabled() const
764 {
765   return m_enableController;
766 }
767 
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)768 void CInputManager::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
769 {
770   if (setting == nullptr)
771     return;
772 
773   const std::string& settingId = setting->GetId();
774   if (settingId == CSettings::SETTING_INPUT_ENABLEMOUSE)
775     m_Mouse.SetEnabled(std::dynamic_pointer_cast<const CSettingBool>(setting)->GetValue());
776 
777   else if (settingId == SETTING_INPUT_ENABLE_CONTROLLER)
778     m_enableController = std::dynamic_pointer_cast<const CSettingBool>(setting)->GetValue();
779 }
780 
OnAction(const CAction & action)781 bool CInputManager::OnAction(const CAction& action)
782 {
783   if (action.GetID() != ACTION_NONE)
784   {
785     if (action.IsAnalog())
786     {
787       QueueAction(action);
788     }
789     else
790     {
791       // If button was pressed this frame, send action
792       if (action.GetHoldTime() == 0)
793       {
794         QueueAction(action);
795       }
796       else
797       {
798         // Only send repeated actions for basic navigation commands
799         bool bIsNavigation = false;
800 
801         switch (action.GetID())
802         {
803           case ACTION_MOVE_LEFT:
804           case ACTION_MOVE_RIGHT:
805           case ACTION_MOVE_UP:
806           case ACTION_MOVE_DOWN:
807           case ACTION_PAGE_UP:
808           case ACTION_PAGE_DOWN:
809             bIsNavigation = true;
810             break;
811 
812           default:
813             break;
814         }
815 
816         if (bIsNavigation)
817           QueueAction(action);
818       }
819     }
820 
821     return true;
822   }
823 
824   return false;
825 }
826 
LoadKeymaps()827 bool CInputManager::LoadKeymaps()
828 {
829   bool bSuccess = false;
830 
831   if (m_buttonTranslator->Load())
832   {
833     bSuccess = true;
834   }
835 
836   SetChanged();
837   NotifyObservers(ObservableMessageButtonMapsChanged);
838 
839   return bSuccess;
840 }
841 
ReloadKeymaps()842 bool CInputManager::ReloadKeymaps()
843 {
844   return LoadKeymaps();
845 }
846 
ClearKeymaps()847 void CInputManager::ClearKeymaps()
848 {
849   m_buttonTranslator->Clear();
850 
851   SetChanged();
852   NotifyObservers(ObservableMessageButtonMapsChanged);
853 }
854 
AddKeymap(const std::string & keymap)855 void CInputManager::AddKeymap(const std::string& keymap)
856 {
857   if (m_buttonTranslator->AddDevice(keymap))
858   {
859     SetChanged();
860     NotifyObservers(ObservableMessageButtonMapsChanged);
861   }
862 }
863 
RemoveKeymap(const std::string & keymap)864 void CInputManager::RemoveKeymap(const std::string& keymap)
865 {
866   if (m_buttonTranslator->RemoveDevice(keymap))
867   {
868     SetChanged();
869     NotifyObservers(ObservableMessageButtonMapsChanged);
870   }
871 }
872 
GetAction(int window,const CKey & key,bool fallback)873 CAction CInputManager::GetAction(int window, const CKey& key, bool fallback /* = true */)
874 {
875   return m_buttonTranslator->GetAction(window, key, fallback);
876 }
877 
TranslateCustomControllerString(int windowId,const std::string & controllerName,int buttonId,int & action,std::string & strAction)878 bool CInputManager::TranslateCustomControllerString(int windowId,
879                                                     const std::string& controllerName,
880                                                     int buttonId,
881                                                     int& action,
882                                                     std::string& strAction)
883 {
884   return m_customControllerTranslator->TranslateCustomControllerString(windowId, controllerName,
885                                                                        buttonId, action, strAction);
886 }
887 
TranslateTouchAction(int windowId,int touchAction,int touchPointers,int & action,std::string & actionString)888 bool CInputManager::TranslateTouchAction(
889     int windowId, int touchAction, int touchPointers, int& action, std::string& actionString)
890 {
891   return m_touchTranslator->TranslateTouchAction(windowId, touchAction, touchPointers, action,
892                                                  actionString);
893 }
894 
GetJoystickKeymaps() const895 std::vector<std::shared_ptr<const IWindowKeymap>> CInputManager::GetJoystickKeymaps() const
896 {
897   return m_joystickTranslator->GetJoystickKeymaps();
898 }
899 
RegisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler * handler)900 void CInputManager::RegisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler* handler)
901 {
902   if (std::find(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler) ==
903       m_keyboardHandlers.end())
904     m_keyboardHandlers.insert(m_keyboardHandlers.begin(), handler);
905 }
906 
UnregisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler * handler)907 void CInputManager::UnregisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler* handler)
908 {
909   m_keyboardHandlers.erase(
910       std::remove(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler),
911       m_keyboardHandlers.end());
912 }
913 
RegisterMouseDriverHandler(MOUSE::IMouseDriverHandler * handler)914 void CInputManager::RegisterMouseDriverHandler(MOUSE::IMouseDriverHandler* handler)
915 {
916   if (std::find(m_mouseHandlers.begin(), m_mouseHandlers.end(), handler) == m_mouseHandlers.end())
917     m_mouseHandlers.insert(m_mouseHandlers.begin(), handler);
918 }
919 
UnregisterMouseDriverHandler(MOUSE::IMouseDriverHandler * handler)920 void CInputManager::UnregisterMouseDriverHandler(MOUSE::IMouseDriverHandler* handler)
921 {
922   m_mouseHandlers.erase(std::remove(m_mouseHandlers.begin(), m_mouseHandlers.end(), handler),
923                         m_mouseHandlers.end());
924 }
925