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.
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"
49 #include <unordered_map>
51 using EVENTSERVER::CEventServer
;
55 const std::string
CInputManager::SETTING_INPUT_ENABLE_CONTROLLER
= "input.enablejoystick";
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());
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()
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
)
119 if (CServiceBroker::GetPeripherals().GetNextKeypress(frameTime
, key
))
124 bool CInputManager::ProcessMouse(int windowId
)
126 if (!m_Mouse
.IsActive() || !g_application
.IsAppFocused())
129 // Get the mouse command ID
130 uint32_t mousekey
= m_Mouse
.GetKey();
131 if (mousekey
== KEY_MOUSE_NOOP
)
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())
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
)
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
);
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)
189 // process any queued up actions
190 if (es
->ExecuteNextAction())
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
;
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)
211 unsigned int wKeyID
= es
->GetButtonCode(strMapName
, isAxis
, fAmount
, isJoystick
);
215 if (strMapName
.length() > 0)
217 // joysticks are not supported via eventserver
222 else // it is a customcontroller
225 std::string actionName
;
227 // Translate using custom controller translator.
228 if (m_customControllerTranslator
->TranslateCustomControllerString(
229 windowId
, strMapName
, wKeyID
, actionID
, actionName
))
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())
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
));
249 CLog::Log(LOGDEBUG
, "ERROR mapping customcontroller action. CustomController: {} {}",
257 if (wKeyID
& ES_FLAG_UNICODE
)
259 key
= CKey(0u, 0u, static_cast<wchar_t>(wKeyID
& ~ES_FLAG_UNICODE
), 0, 0, 0, 0);
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
);
285 key
.SetFromService(true);
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
));
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
);
348 bool CInputManager::OnEvent(XBMC_Event
& newEvent
)
350 switch (newEvent
.type
)
354 m_Keyboard
.ProcessKeyDown(newEvent
.key
.keysym
);
355 CKey key
= m_Keyboard
.TranslateKey(newEvent
.key
.keysym
);
360 m_Keyboard
.ProcessKeyUp();
361 OnKeyUp(m_Keyboard
.TranslateKey(newEvent
.key
.keysym
));
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
);
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
))
388 case XBMC_MOUSEBUTTONDOWN
:
390 MOUSE::BUTTON_ID buttonId
;
391 if (CMouseTranslator::TranslateEventID(newEvent
.button
.button
, buttonId
))
393 if (driverHandler
->OnButtonPress(buttonId
))
398 case XBMC_MOUSEBUTTONUP
:
400 MOUSE::BUTTON_ID buttonId
;
401 if (CMouseTranslator::TranslateEventID(newEvent
.button
.button
, buttonId
))
402 driverHandler
->OnButtonRelease(buttonId
);
415 m_Mouse
.HandleEvent(newEvent
);
416 ProcessMouse(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog());
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));
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
;
435 int iWin
= CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog();
436 m_touchTranslator
->TranslateTouchAction(iWin
, newEvent
.touch
.action
,
437 newEvent
.touch
.pointers
, actionId
, actionString
);
443 if ((actionId
>= ACTION_TOUCH_TAP
&& actionId
<= ACTION_GESTURE_END
) ||
444 (actionId
>= ACTION_MOUSE_START
&& actionId
<= ACTION_MOUSE_END
))
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
));
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
)));
459 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION
, WINDOW_INVALID
, -1,
460 static_cast<void*>(new CAction(actionId
)));
468 m_buttonStat
.TranslateKey(CKey(newEvent
.keybutton
.button
, newEvent
.keybutton
.holdtime
)));
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
))
500 if (key
.GetButtonCode() == m_LastKey
.GetButtonCode() &&
501 (m_LastKey
.GetButtonCode() & CKey::MODIFIER_LONG
))
503 // Do not repeat long presses
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
))
514 bHandled
= HandleKey(key
);
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
);
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.
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(),
561 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_CECTOGGLESTATE
, 0, 0,
562 static_cast<void*>(&result
));
568 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_CECSTANDBY
);
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()));
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
591 key
.FromKeyboard() && (iWin
== WINDOW_DIALOG_KEYBOARD
|| iWin
== WINDOW_DIALOG_NUMERIC
);
592 CGUIWindow
* window
= CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iWin
);
595 CGUIControl
* control
= window
->GetFocusedControl();
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
)
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
&&
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
632 if (key
.GetFromService())
633 action
= CAction(key
.GetButtonCode() != KEY_INVALID
? key
.GetButtonCode() : 0,
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
)
648 // Placeholder for other operating systems
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
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
))
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
);
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
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());
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")
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
);
739 gui
->GetAudioManager().PlayActionSound(action
);
744 gui
->GetAudioManager().PlayActionSound(action
);
746 bResult
= g_application
.OnAction(action
);
751 bool CInputManager::HasBuiltin(const std::string
& command
)
756 int CInputManager::ExecuteBuiltin(const std::string
& execute
,
757 const std::vector
<std::string
>& params
)
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)
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())
825 // If button was pressed this frame, send action
826 if (action
.GetHoldTime() == 0)
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
:
840 case ACTION_MOVE_DOWN
:
842 case ACTION_PAGE_DOWN
:
843 bIsNavigation
= true;
861 bool CInputManager::LoadKeymaps()
863 bool bSuccess
= false;
865 if (m_buttonTranslator
->Load())
871 NotifyObservers(ObservableMessageButtonMapsChanged
);
876 bool CInputManager::ReloadKeymaps()
878 return LoadKeymaps();
881 void CInputManager::ClearKeymaps()
883 m_buttonTranslator
->Clear();
886 NotifyObservers(ObservableMessageButtonMapsChanged
);
889 void CInputManager::AddKeymap(const std::string
& keymap
)
891 if (m_buttonTranslator
->AddDevice(keymap
))
894 NotifyObservers(ObservableMessageButtonMapsChanged
);
898 void CInputManager::RemoveKeymap(const std::string
& keymap
)
900 if (m_buttonTranslator
->RemoveDevice(keymap
))
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
,
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
,
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());