Merge pull request #26148 from ksooo/fix-secondstotimestring-warning
[xbmc.git] / xbmc / input / InputManager.cpp
bloba07a1316ee694bd40c0daaa66062f8f03cf43142
1 /*
2 * Copyright (C) 2005-2024 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "InputManager.h"
11 #include "ServiceBroker.h"
12 #include "application/AppInboundProtocol.h"
13 #include "application/Application.h"
14 #include "application/ApplicationComponents.h"
15 #include "application/ApplicationPowerHandling.h"
16 #include "guilib/GUIAudioManager.h"
17 #include "guilib/GUIComponent.h"
18 #include "guilib/GUIControl.h"
19 #include "guilib/GUIWindow.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "input/actions/Action.h"
22 #include "input/actions/ActionIDs.h"
23 #include "input/keyboard/Key.h"
24 #include "input/keyboard/KeyIDs.h"
25 #include "input/keyboard/KeyboardEasterEgg.h"
26 #include "input/keyboard/XBMC_vkeys.h"
27 #include "input/keyboard/interfaces/IKeyboardDriverHandler.h"
28 #include "input/keymaps/ButtonTranslator.h"
29 #include "input/keymaps/KeymapEnvironment.h"
30 #include "input/keymaps/joysticks/JoystickMapper.h"
31 #include "input/keymaps/remote/CustomControllerTranslator.h"
32 #include "input/keymaps/touch/TouchTranslator.h"
33 #include "input/mouse/MouseTranslator.h"
34 #include "input/mouse/interfaces/IMouseDriverHandler.h"
35 #include "messaging/ApplicationMessenger.h"
36 #include "network/EventServer.h"
37 #include "peripherals/Peripherals.h"
38 #include "settings/Settings.h"
39 #include "settings/SettingsComponent.h"
40 #include "settings/lib/Setting.h"
41 #include "utils/ExecString.h"
42 #include "utils/Geometry.h"
43 #include "utils/StringUtils.h"
44 #include "utils/log.h"
46 #include <algorithm>
47 #include <math.h>
48 #include <mutex>
49 #include <unordered_map>
51 using EVENTSERVER::CEventServer;
53 using namespace KODI;
55 const std::string CInputManager::SETTING_INPUT_ENABLE_CONTROLLER = "input.enablejoystick";
57 namespace
59 const std::unordered_map<uint8_t, int> keyComposeactionEventMap = {
60 {XBMC_KEYCOMPOSING_COMPOSING, ACTION_KEYBOARD_COMPOSING_KEY},
61 {XBMC_KEYCOMPOSING_CANCELLED, ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED},
62 {XBMC_KEYCOMPOSING_FINISHED, ACTION_KEYBOARD_COMPOSING_KEY_FINISHED}};
65 CInputManager::CInputManager()
66 : m_keymapEnvironment(new KEYMAP::CKeymapEnvironment),
67 m_buttonTranslator(new KEYMAP::CButtonTranslator),
68 m_customControllerTranslator(new KEYMAP::CCustomControllerTranslator),
69 m_touchTranslator(new KEYMAP::CTouchTranslator),
70 m_joystickTranslator(new KEYMAP::CJoystickMapper),
71 m_keyboardEasterEgg(new KEYBOARD::CKeyboardEasterEgg)
73 m_buttonTranslator->RegisterMapper("touch", m_touchTranslator.get());
74 m_buttonTranslator->RegisterMapper("customcontroller", m_customControllerTranslator.get());
75 m_buttonTranslator->RegisterMapper("joystick", m_joystickTranslator.get());
77 RegisterKeyboardDriverHandler(m_keyboardEasterEgg.get());
79 // Register settings
80 std::set<std::string> settingSet;
81 settingSet.insert(CSettings::SETTING_INPUT_ENABLEMOUSE);
82 settingSet.insert(SETTING_INPUT_ENABLE_CONTROLLER);
83 CServiceBroker::GetSettingsComponent()->GetSettings()->RegisterCallback(this, settingSet);
86 CInputManager::~CInputManager()
88 Deinitialize();
90 // Unregister settings
91 CServiceBroker::GetSettingsComponent()->GetSettings()->UnregisterCallback(this);
93 UnregisterKeyboardDriverHandler(m_keyboardEasterEgg.get());
95 m_buttonTranslator->UnregisterMapper(m_touchTranslator.get());
96 m_buttonTranslator->UnregisterMapper(m_customControllerTranslator.get());
97 m_buttonTranslator->UnregisterMapper(m_joystickTranslator.get());
100 void CInputManager::InitializeInputs()
102 m_Keyboard.Initialize();
104 m_Mouse.Initialize();
105 m_Mouse.SetEnabled(CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
106 CSettings::SETTING_INPUT_ENABLEMOUSE));
108 m_enableController = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
109 SETTING_INPUT_ENABLE_CONTROLLER);
112 void CInputManager::Deinitialize()
116 bool CInputManager::ProcessPeripherals(float frameTime)
118 CKey key;
119 if (CServiceBroker::GetPeripherals().GetNextKeypress(frameTime, key))
120 return OnKey(key);
121 return false;
124 bool CInputManager::ProcessMouse(int windowId)
126 if (!m_Mouse.IsActive() || !g_application.IsAppFocused())
127 return false;
129 // Get the mouse command ID
130 uint32_t mousekey = m_Mouse.GetKey();
131 if (mousekey == KEY_MOUSE_NOOP)
132 return true;
134 // Reset the screensaver and idle timers
135 auto& components = CServiceBroker::GetAppComponents();
136 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
137 appPower->ResetSystemIdleTimer();
138 appPower->ResetScreenSaver();
140 if (appPower->WakeUpScreenSaverAndDPMS())
141 return true;
143 // Retrieve the corresponding action
144 CKey key(mousekey, (unsigned int)0);
145 CAction mouseaction = m_buttonTranslator->GetAction(windowId, key);
147 // Deactivate mouse if non-mouse action
148 if (!mouseaction.IsMouse())
149 m_Mouse.SetActive(false);
151 // Consume ACTION_NOOP.
152 // Some views or dialogs gets closed after any ACTION and
153 // a sensitive mouse might cause problems.
154 if (mouseaction.GetID() == ACTION_NOOP)
155 return false;
157 // If we couldn't find an action return false to indicate we have not
158 // handled this mouse action
159 if (!mouseaction.GetID())
161 CLog::LogF(LOGDEBUG, "unknown mouse command {}", mousekey);
162 return false;
165 // Log mouse actions except for move and noop
166 if (mouseaction.GetID() != ACTION_MOUSE_MOVE && mouseaction.GetID() != ACTION_NOOP)
167 CLog::LogF(LOGDEBUG, "trying mouse action {}", mouseaction.GetName());
169 // The action might not be a mouse action. For example wheel moves might
170 // be mapped to volume up/down in mouse.xml. In this case we do not want
171 // the mouse position saved in the action.
172 if (!mouseaction.IsMouse())
173 return g_application.OnAction(mouseaction);
175 // This is a mouse action so we need to record the mouse position
176 return g_application.OnAction(
177 CAction(mouseaction.GetID(), static_cast<uint32_t>(m_Mouse.GetHold(MOUSE_LEFT_BUTTON)),
178 static_cast<float>(m_Mouse.GetX()), static_cast<float>(m_Mouse.GetY()),
179 static_cast<float>(m_Mouse.GetDX()), static_cast<float>(m_Mouse.GetDY()), 0.0f, 0.0f,
180 mouseaction.GetName()));
183 bool CInputManager::ProcessEventServer(int windowId, float frameTime)
185 CEventServer* es = CEventServer::GetInstance();
186 if (!es || !es->Running() || es->GetNumberOfClients() == 0)
187 return false;
189 // process any queued up actions
190 if (es->ExecuteNextAction())
192 // reset idle timers
193 auto& components = CServiceBroker::GetAppComponents();
194 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
195 appPower->ResetSystemIdleTimer();
196 appPower->ResetScreenSaver();
197 appPower->WakeUpScreenSaverAndDPMS();
200 // now handle any buttons or axis
201 std::string strMapName;
202 bool isAxis = false;
203 float fAmount = 0.0;
204 bool isJoystick = false;
206 // es->ExecuteNextAction() invalidates the ref to the CEventServer instance
207 // when the action exits XBMC
208 es = CEventServer::GetInstance();
209 if (!es || !es->Running() || es->GetNumberOfClients() == 0)
210 return false;
211 unsigned int wKeyID = es->GetButtonCode(strMapName, isAxis, fAmount, isJoystick);
213 if (wKeyID)
215 if (strMapName.length() > 0)
217 // joysticks are not supported via eventserver
218 if (isJoystick)
220 return false;
222 else // it is a customcontroller
224 int actionID;
225 std::string actionName;
227 // Translate using custom controller translator.
228 if (m_customControllerTranslator->TranslateCustomControllerString(
229 windowId, strMapName, wKeyID, actionID, actionName))
231 // break screensaver
232 auto& components = CServiceBroker::GetAppComponents();
233 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
234 appPower->ResetSystemIdleTimer();
235 appPower->ResetScreenSaver();
237 // in case we wokeup the screensaver or screen - eat that action...
238 if (appPower->WakeUpScreenSaverAndDPMS())
239 return true;
241 m_Mouse.SetActive(false);
243 CLog::Log(LOGDEBUG, "EventServer: key {} translated to action {}", wKeyID, actionName);
245 return ExecuteInputAction(CAction(actionID, fAmount, 0.0f, actionName, 0, wKeyID));
247 else
249 CLog::Log(LOGDEBUG, "ERROR mapping customcontroller action. CustomController: {} {}",
250 strMapName, wKeyID);
254 else
256 CKey key;
257 if (wKeyID & ES_FLAG_UNICODE)
259 key = CKey(0u, 0u, static_cast<wchar_t>(wKeyID & ~ES_FLAG_UNICODE), 0, 0, 0, 0);
260 return OnKey(key);
263 if (wKeyID == KEY_BUTTON_LEFT_ANALOG_TRIGGER)
264 key = CKey(wKeyID, static_cast<uint8_t>(255 * fAmount), 0, 0.0, 0.0, 0.0, 0.0, frameTime);
265 else if (wKeyID == KEY_BUTTON_RIGHT_ANALOG_TRIGGER)
266 key = CKey(wKeyID, 0, static_cast<uint8_t>(255 * fAmount), 0.0, 0.0, 0.0, 0.0, frameTime);
267 else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_LEFT)
268 key = CKey(wKeyID, 0, 0, -fAmount, 0.0, 0.0, 0.0, frameTime);
269 else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_RIGHT)
270 key = CKey(wKeyID, 0, 0, fAmount, 0.0, 0.0, 0.0, frameTime);
271 else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_UP)
272 key = CKey(wKeyID, 0, 0, 0.0, fAmount, 0.0, 0.0, frameTime);
273 else if (wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_DOWN)
274 key = CKey(wKeyID, 0, 0, 0.0, -fAmount, 0.0, 0.0, frameTime);
275 else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_LEFT)
276 key = CKey(wKeyID, 0, 0, 0.0, 0.0, -fAmount, 0.0, frameTime);
277 else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT)
278 key = CKey(wKeyID, 0, 0, 0.0, 0.0, fAmount, 0.0, frameTime);
279 else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_UP)
280 key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, fAmount, frameTime);
281 else if (wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_DOWN)
282 key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, -fAmount, frameTime);
283 else
284 key = CKey(wKeyID);
285 key.SetFromService(true);
286 return OnKey(key);
291 CPoint pos;
292 if (es->GetMousePos(pos.x, pos.y) && m_Mouse.IsEnabled())
294 XBMC_Event newEvent = {};
295 newEvent.type = XBMC_MOUSEMOTION;
296 newEvent.motion.x = (uint16_t)pos.x;
297 newEvent.motion.y = (uint16_t)pos.y;
298 CServiceBroker::GetAppPort()->OnEvent(
299 newEvent); // had to call this to update g_Mouse position
300 return g_application.OnAction(CAction(ACTION_MOUSE_MOVE, pos.x, pos.y));
304 return false;
307 void CInputManager::ProcessQueuedActions()
309 std::vector<CAction> queuedActions;
311 std::unique_lock<CCriticalSection> lock(m_actionMutex);
312 queuedActions.swap(m_queuedActions);
315 for (const CAction& action : queuedActions)
316 g_application.OnAction(action);
319 void CInputManager::QueueAction(const CAction& action)
321 std::unique_lock<CCriticalSection> lock(m_actionMutex);
323 // Avoid dispatching multiple analog actions per frame with the same ID
324 if (action.IsAnalog())
326 m_queuedActions.erase(std::remove_if(m_queuedActions.begin(), m_queuedActions.end(),
327 [&action](const CAction& queuedAction)
328 { return action.GetID() == queuedAction.GetID(); }),
329 m_queuedActions.end());
332 m_queuedActions.push_back(action);
335 bool CInputManager::Process(int windowId, float frameTime)
337 // process input actions
338 ProcessEventServer(windowId, frameTime);
339 ProcessPeripherals(frameTime);
340 ProcessQueuedActions();
342 // Inform the environment of the new active window ID
343 m_keymapEnvironment->SetWindowID(windowId);
345 return true;
348 bool CInputManager::OnEvent(XBMC_Event& newEvent)
350 switch (newEvent.type)
352 case XBMC_KEYDOWN:
354 m_Keyboard.ProcessKeyDown(newEvent.key.keysym);
355 CKey key = m_Keyboard.TranslateKey(newEvent.key.keysym);
356 OnKey(key);
357 break;
359 case XBMC_KEYUP:
360 m_Keyboard.ProcessKeyUp();
361 OnKeyUp(m_Keyboard.TranslateKey(newEvent.key.keysym));
362 break;
363 case XBMC_KEYCOMPOSING_COMPOSING:
364 case XBMC_KEYCOMPOSING_CANCELLED:
365 case XBMC_KEYCOMPOSING_FINISHED:
367 const CAction action = CAction(keyComposeactionEventMap.find(newEvent.type)->second,
368 static_cast<wchar_t>(newEvent.key.keysym.unicode));
369 ExecuteInputAction(action);
370 break;
372 case XBMC_MOUSEBUTTONDOWN:
373 case XBMC_MOUSEBUTTONUP:
374 case XBMC_MOUSEMOTION:
376 bool handled = false;
378 for (auto driverHandler : m_mouseHandlers)
380 switch (newEvent.type)
382 case XBMC_MOUSEMOTION:
384 if (driverHandler->OnPosition(newEvent.motion.x, newEvent.motion.y))
385 handled = true;
386 break;
388 case XBMC_MOUSEBUTTONDOWN:
390 MOUSE::BUTTON_ID buttonId;
391 if (CMouseTranslator::TranslateEventID(newEvent.button.button, buttonId))
393 if (driverHandler->OnButtonPress(buttonId))
394 handled = true;
396 break;
398 case XBMC_MOUSEBUTTONUP:
400 MOUSE::BUTTON_ID buttonId;
401 if (CMouseTranslator::TranslateEventID(newEvent.button.button, buttonId))
402 driverHandler->OnButtonRelease(buttonId);
403 break;
405 default:
406 break;
409 if (handled)
410 break;
413 if (!handled)
415 m_Mouse.HandleEvent(newEvent);
416 ProcessMouse(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
418 break;
420 case XBMC_TOUCH:
422 if (newEvent.touch.action == ACTION_TOUCH_TAP)
423 { // Send a mouse motion event with no dx,dy for getting the current guiitem selected
424 g_application.OnAction(
425 CAction(ACTION_MOUSE_MOVE, 0, newEvent.touch.x, newEvent.touch.y, 0, 0));
427 int actionId = 0;
428 std::string actionString;
429 if (newEvent.touch.action == ACTION_GESTURE_BEGIN ||
430 newEvent.touch.action == ACTION_GESTURE_END ||
431 newEvent.touch.action == ACTION_GESTURE_ABORT)
432 actionId = newEvent.touch.action;
433 else
435 int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
436 m_touchTranslator->TranslateTouchAction(iWin, newEvent.touch.action,
437 newEvent.touch.pointers, actionId, actionString);
440 if (actionId <= 0)
441 return false;
443 if ((actionId >= ACTION_TOUCH_TAP && actionId <= ACTION_GESTURE_END) ||
444 (actionId >= ACTION_MOUSE_START && actionId <= ACTION_MOUSE_END))
446 auto action =
447 new CAction(actionId, 0, newEvent.touch.x, newEvent.touch.y, newEvent.touch.x2,
448 newEvent.touch.y2, newEvent.touch.x3, newEvent.touch.y3);
449 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
450 static_cast<void*>(action));
452 else
454 if (actionId == ACTION_BUILT_IN_FUNCTION && !actionString.empty())
455 CServiceBroker::GetAppMessenger()->PostMsg(
456 TMSG_GUI_ACTION, WINDOW_INVALID, -1,
457 static_cast<void*>(new CAction(actionId, actionString)));
458 else
459 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1,
460 static_cast<void*>(new CAction(actionId)));
463 break;
464 } // case
465 case XBMC_BUTTON:
467 HandleKey(
468 m_buttonStat.TranslateKey(CKey(newEvent.keybutton.button, newEvent.keybutton.holdtime)));
469 break;
471 } // switch
473 return true;
476 // OnKey() translates the key into a CAction which is sent on to our Window Manager.
477 // The window manager will return true if the event is processed, false otherwise.
478 // If not already processed, this routine handles global keypresses. It returns
479 // true if the key has been processed, false otherwise.
481 bool CInputManager::OnKey(const CKey& key)
483 bool bHandled = false;
485 for (auto handler : m_keyboardHandlers)
487 if (handler->OnKeyPress(key))
489 bHandled = true;
490 break;
494 if (bHandled)
496 m_LastKey.Reset();
498 else
500 if (key.GetButtonCode() == m_LastKey.GetButtonCode() &&
501 (m_LastKey.GetButtonCode() & CKey::MODIFIER_LONG))
503 // Do not repeat long presses
505 else
507 // Event server keyboard doesn't give normal key up and key down, so don't
508 // process for long press if that is the source
509 if (key.GetFromService() ||
510 !m_buttonTranslator->HasLongpressMapping(
511 CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog(), key))
513 m_LastKey.Reset();
514 bHandled = HandleKey(key);
516 else
518 if (key.GetButtonCode() != m_LastKey.GetButtonCode() &&
519 (key.GetButtonCode() & CKey::MODIFIER_LONG))
521 m_LastKey = key; // OnKey is reentrant; need to do this before entering
522 bHandled = HandleKey(key);
525 m_LastKey = key;
530 return bHandled;
533 bool CInputManager::HandleKey(const CKey& key)
535 // Turn the mouse off, as we've just got a keypress from controller or remote
536 m_Mouse.SetActive(false);
538 // get the current active window
539 int iWin = CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
541 // this will be checked for certain keycodes that need
542 // special handling if the screensaver is active
543 CAction action = m_buttonTranslator->GetAction(iWin, key);
545 // a key has been pressed.
546 // reset Idle Timer
547 auto& components = CServiceBroker::GetAppComponents();
548 const auto appPower = components.GetComponent<CApplicationPowerHandling>();
549 appPower->ResetSystemIdleTimer();
550 bool processKey = AlwaysProcess(action);
552 if (StringUtils::StartsWithNoCase(action.GetName(), "CECToggleState") ||
553 StringUtils::StartsWithNoCase(action.GetName(), "CECStandby"))
555 // do not wake up the screensaver right after switching off the playing device
556 if (StringUtils::StartsWithNoCase(action.GetName(), "CECToggleState"))
558 CLog::LogF(LOGDEBUG, "action {} [{}], toggling state of playing device", action.GetName(),
559 action.GetID());
560 bool result;
561 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_CECTOGGLESTATE, 0, 0,
562 static_cast<void*>(&result));
563 if (!result)
564 return true;
566 else
568 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_CECSTANDBY);
569 return true;
573 appPower->ResetScreenSaver();
575 // allow some keys to be processed while the screensaver is active
576 if (appPower->WakeUpScreenSaverAndDPMS(processKey) && !processKey)
578 CLog::LogF(LOGDEBUG, "{} pressed, screen saver/dpms woken up",
579 m_Keyboard.GetKeyName((int)key.GetButtonCode()));
580 return true;
583 if (iWin != WINDOW_FULLSCREEN_VIDEO && iWin != WINDOW_FULLSCREEN_GAME)
585 // current active window isnt the fullscreen window
586 // just use corresponding section from keymap.xml
587 // to map key->action
589 // first determine if we should use keyboard input directly
590 bool useKeyboard =
591 key.FromKeyboard() && (iWin == WINDOW_DIALOG_KEYBOARD || iWin == WINDOW_DIALOG_NUMERIC);
592 CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iWin);
593 if (window)
595 CGUIControl* control = window->GetFocusedControl();
596 if (control)
598 // If this is an edit control set usekeyboard to true. This causes the
599 // keypress to be processed directly not through the key mappings.
600 if (control->GetControlType() == CGUIControl::GUICONTROL_EDIT)
601 useKeyboard = true;
603 // If the key pressed is shift-A to shift-Z set usekeyboard to true.
604 // This causes the keypress to be used for list navigation.
605 if (control->IsContainer() && key.GetModifiers() == CKey::MODIFIER_SHIFT &&
606 key.GetUnicode())
607 useKeyboard = true;
610 if (useKeyboard)
612 // use the virtualkeyboard section of the keymap, and send keyboard-specific or navigation
613 // actions through if that's what they are
614 CAction action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key);
615 if (!(action.GetID() == ACTION_MOVE_LEFT || action.GetID() == ACTION_MOVE_RIGHT ||
616 action.GetID() == ACTION_MOVE_UP || action.GetID() == ACTION_MOVE_DOWN ||
617 action.GetID() == ACTION_SELECT_ITEM || action.GetID() == ACTION_ENTER ||
618 action.GetID() == ACTION_PREVIOUS_MENU || action.GetID() == ACTION_NAV_BACK ||
619 action.GetID() == ACTION_VOICE_RECOGNIZE))
621 // the action isn't plain navigation - check for a keyboard-specific keymap
622 action = m_buttonTranslator->GetAction(WINDOW_DIALOG_KEYBOARD, key, false);
623 if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) ||
624 action.GetID() == ACTION_BACKSPACE || action.GetID() == ACTION_SHIFT ||
625 action.GetID() == ACTION_SYMBOLS || action.GetID() == ACTION_CURSOR_LEFT ||
626 action.GetID() == ACTION_CURSOR_RIGHT)
627 action = CAction(0); // don't bother with this action
629 // else pass the keys through directly
630 if (!action.GetID())
632 if (key.GetFromService())
633 action = CAction(key.GetButtonCode() != KEY_INVALID ? key.GetButtonCode() : 0,
634 key.GetUnicode());
635 else
637 // Check for paste keypress
638 #ifdef TARGET_WINDOWS
639 // In Windows paste is ctrl-V
640 if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL)
641 #elif defined(TARGET_LINUX)
642 // In Linux paste is ctrl-V
643 if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL)
644 #elif defined(TARGET_DARWIN_OSX)
645 // In OSX paste is cmd-V
646 if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_META)
647 #else
648 // Placeholder for other operating systems
649 if (false)
650 #endif
651 action = CAction(ACTION_PASTE);
652 // If the unicode is non-zero the keypress is a non-printing character
653 else if (key.GetUnicode())
654 action = CAction(KEY_UNICODE, key.GetUnicode());
655 // The keypress is a non-printing character
656 else
657 action = CAction(key.GetVKey() | KEY_VKEY);
661 CLog::LogF(LOGDEBUG, "{} pressed, trying keyboard action {:x}",
662 m_Keyboard.GetKeyName((int)key.GetButtonCode()), action.GetID());
664 if (g_application.OnAction(action))
665 return true;
666 // failed to handle the keyboard action, drop down through to standard action
668 if (key.GetFromService())
670 if (key.GetButtonCode() != KEY_INVALID)
671 action = m_buttonTranslator->GetAction(iWin, key);
673 else
674 action = m_buttonTranslator->GetAction(iWin, key);
676 if (!key.IsAnalogButton())
677 CLog::LogF(LOGDEBUG, "{} pressed, window {}, action is {}",
678 m_Keyboard.GetKeyName((int)key.GetButtonCode()), iWin, action.GetName());
680 return ExecuteInputAction(action);
683 void CInputManager::OnKeyUp(const CKey& key)
685 for (auto handler : m_keyboardHandlers)
686 handler->OnKeyRelease(key);
688 if (m_LastKey.GetButtonCode() != KEY_INVALID &&
689 !(m_LastKey.GetButtonCode() & CKey::MODIFIER_LONG))
691 CKey key = m_LastKey;
692 m_LastKey.Reset(); // OnKey is reentrant; need to do this before entering
693 HandleKey(key);
695 else
696 m_LastKey.Reset();
699 bool CInputManager::AlwaysProcess(const CAction& action)
701 // check if this button is mapped to a built-in function
702 if (!action.GetName().empty())
704 const CExecString exec(action.GetName());
705 if (exec.IsValid())
707 const std::string& builtInFunction = exec.GetFunction();
709 // should this button be handled normally or just cancel the screensaver?
710 if (builtInFunction == "powerdown" || builtInFunction == "reboot" ||
711 builtInFunction == "restart" || builtInFunction == "restartapp" ||
712 builtInFunction == "suspend" || builtInFunction == "hibernate" ||
713 builtInFunction == "quit" || builtInFunction == "shutdown" ||
714 builtInFunction == "volumeup" || builtInFunction == "volumedown" ||
715 builtInFunction == "mute" || builtInFunction == "RunAppleScript" ||
716 builtInFunction == "RunAddon" || builtInFunction == "RunPlugin" ||
717 builtInFunction == "RunScript" || builtInFunction == "System.Exec" ||
718 builtInFunction == "System.ExecWait")
720 return true;
725 return false;
728 bool CInputManager::ExecuteInputAction(const CAction& action)
730 bool bResult = false;
731 CGUIComponent* gui = CServiceBroker::GetGUI();
733 // play sound before the action unless the button is held,
734 // where we execute after the action as held actions aren't fired every time.
735 if (action.GetHoldTime())
737 bResult = g_application.OnAction(action);
738 if (bResult && gui)
739 gui->GetAudioManager().PlayActionSound(action);
741 else
743 if (gui)
744 gui->GetAudioManager().PlayActionSound(action);
746 bResult = g_application.OnAction(action);
748 return bResult;
751 bool CInputManager::HasBuiltin(const std::string& command)
753 return false;
756 int CInputManager::ExecuteBuiltin(const std::string& execute,
757 const std::vector<std::string>& params)
759 return 0;
762 void CInputManager::SetMouseActive(bool active /* = true */)
764 m_Mouse.SetActive(active);
767 void CInputManager::SetMouseEnabled(bool mouseEnabled /* = true */)
769 m_Mouse.SetEnabled(mouseEnabled);
772 bool CInputManager::IsMouseActive()
774 return m_Mouse.IsActive();
777 MOUSE_STATE CInputManager::GetMouseState()
779 return m_Mouse.GetState();
782 MousePosition CInputManager::GetMousePosition()
784 return m_Mouse.GetPosition();
787 void CInputManager::SetMouseResolution(int maxX, int maxY, float speedX, float speedY)
789 m_Mouse.SetResolution(maxX, maxY, speedX, speedY);
792 void CInputManager::SetMouseState(MOUSE_STATE mouseState)
794 m_Mouse.SetState(mouseState);
797 bool CInputManager::IsControllerEnabled() const
799 return m_enableController;
802 void CInputManager::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
804 if (setting == nullptr)
805 return;
807 const std::string& settingId = setting->GetId();
808 if (settingId == CSettings::SETTING_INPUT_ENABLEMOUSE)
809 m_Mouse.SetEnabled(std::dynamic_pointer_cast<const CSettingBool>(setting)->GetValue());
811 else if (settingId == SETTING_INPUT_ENABLE_CONTROLLER)
812 m_enableController = std::dynamic_pointer_cast<const CSettingBool>(setting)->GetValue();
815 bool CInputManager::OnAction(const CAction& action)
817 if (action.GetID() != ACTION_NONE)
819 if (action.IsAnalog())
821 QueueAction(action);
823 else
825 // If button was pressed this frame, send action
826 if (action.GetHoldTime() == 0)
828 QueueAction(action);
830 else
832 // Only send repeated actions for basic navigation commands
833 bool bIsNavigation = false;
835 switch (action.GetID())
837 case ACTION_MOVE_LEFT:
838 case ACTION_MOVE_RIGHT:
839 case ACTION_MOVE_UP:
840 case ACTION_MOVE_DOWN:
841 case ACTION_PAGE_UP:
842 case ACTION_PAGE_DOWN:
843 bIsNavigation = true;
844 break;
846 default:
847 break;
850 if (bIsNavigation)
851 QueueAction(action);
855 return true;
858 return false;
861 bool CInputManager::LoadKeymaps()
863 bool bSuccess = false;
865 if (m_buttonTranslator->Load())
867 bSuccess = true;
870 SetChanged();
871 NotifyObservers(ObservableMessageButtonMapsChanged);
873 return bSuccess;
876 bool CInputManager::ReloadKeymaps()
878 return LoadKeymaps();
881 void CInputManager::ClearKeymaps()
883 m_buttonTranslator->Clear();
885 SetChanged();
886 NotifyObservers(ObservableMessageButtonMapsChanged);
889 void CInputManager::AddKeymap(const std::string& keymap)
891 if (m_buttonTranslator->AddDevice(keymap))
893 SetChanged();
894 NotifyObservers(ObservableMessageButtonMapsChanged);
898 void CInputManager::RemoveKeymap(const std::string& keymap)
900 if (m_buttonTranslator->RemoveDevice(keymap))
902 SetChanged();
903 NotifyObservers(ObservableMessageButtonMapsChanged);
907 const KEYMAP::IKeymapEnvironment* CInputManager::KeymapEnvironment() const
909 return m_keymapEnvironment.get();
912 CAction CInputManager::GetAction(int window, const CKey& key, bool fallback /* = true */)
914 return m_buttonTranslator->GetAction(window, key, fallback);
917 bool CInputManager::TranslateCustomControllerString(int windowId,
918 const std::string& controllerName,
919 int buttonId,
920 int& action,
921 std::string& strAction)
923 return m_customControllerTranslator->TranslateCustomControllerString(windowId, controllerName,
924 buttonId, action, strAction);
927 bool CInputManager::TranslateTouchAction(
928 int windowId, int touchAction, int touchPointers, int& action, std::string& actionString)
930 return m_touchTranslator->TranslateTouchAction(windowId, touchAction, touchPointers, action,
931 actionString);
934 std::vector<std::shared_ptr<const KEYMAP::IWindowKeymap>> CInputManager::GetJoystickKeymaps() const
936 return m_joystickTranslator->GetJoystickKeymaps();
939 void CInputManager::RegisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler* handler)
941 if (std::find(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler) ==
942 m_keyboardHandlers.end())
943 m_keyboardHandlers.insert(m_keyboardHandlers.begin(), handler);
946 void CInputManager::UnregisterKeyboardDriverHandler(KEYBOARD::IKeyboardDriverHandler* handler)
948 m_keyboardHandlers.erase(
949 std::remove(m_keyboardHandlers.begin(), m_keyboardHandlers.end(), handler),
950 m_keyboardHandlers.end());
953 void CInputManager::RegisterMouseDriverHandler(MOUSE::IMouseDriverHandler* handler)
955 if (std::find(m_mouseHandlers.begin(), m_mouseHandlers.end(), handler) == m_mouseHandlers.end())
956 m_mouseHandlers.insert(m_mouseHandlers.begin(), handler);
959 void CInputManager::UnregisterMouseDriverHandler(MOUSE::IMouseDriverHandler* handler)
961 m_mouseHandlers.erase(std::remove(m_mouseHandlers.begin(), m_mouseHandlers.end(), handler),
962 m_mouseHandlers.end());