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