InputComponent.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #include "QsLog.h"
  2. #include "InputComponent.h"
  3. #include "settings/SettingsComponent.h"
  4. #include "system/SystemComponent.h"
  5. #include "power/PowerComponent.h"
  6. #include "InputKeyboard.h"
  7. #include "InputSocket.h"
  8. #include "InputRoku.h"
  9. #ifdef Q_OS_MAC
  10. #include "apple/InputAppleRemote.h"
  11. #include "apple/InputAppleMediaKeys.h"
  12. #endif
  13. #ifdef HAVE_SDL
  14. #include "InputSDL.h"
  15. #endif
  16. #ifdef HAVE_LIRC
  17. #include "InputLIRC.h"
  18. #endif
  19. #ifdef HAVE_CEC
  20. #include "InputCEC.h"
  21. #endif
  22. #define LONG_HOLD_MSEC 500
  23. ///////////////////////////////////////////////////////////////////////////////////////////////////
  24. InputComponent::InputComponent(QObject* parent) : ComponentBase(parent), m_currentActionCount(0)
  25. {
  26. m_mappings = new InputMapping(this);
  27. }
  28. ///////////////////////////////////////////////////////////////////////////////////////////////////
  29. bool InputComponent::addInput(InputBase* base)
  30. {
  31. if (!base->initInput())
  32. {
  33. QLOG_WARN() << "Failed to init input:" << base->inputName();
  34. return false;
  35. }
  36. QLOG_INFO() << "Successfully inited input:" << base->inputName();
  37. m_inputs.push_back(base);
  38. // we connect to the provider receivedInput signal, then we check if the name
  39. // needs to be remaped in remapInput and then finally send it out to JS land.
  40. //
  41. connect(base, &InputBase::receivedInput, this, &InputComponent::remapInput);
  42. // for auto-repeating inputs
  43. //
  44. m_autoRepeatTimer = new QTimer(this);
  45. connect(m_autoRepeatTimer, &QTimer::timeout, [=]()
  46. {
  47. if (!m_currentAction.isEmpty())
  48. {
  49. m_currentActionCount ++;
  50. emit receivedAction(m_currentAction);
  51. }
  52. qint32 multiplier = qMin(5, qMax(1, m_currentActionCount / 5));
  53. m_autoRepeatTimer->setInterval(100 / multiplier);
  54. });
  55. return true;
  56. }
  57. ///////////////////////////////////////////////////////////////////////////////////////////////////
  58. bool InputComponent::componentInitialize()
  59. {
  60. // load our input mappings
  61. m_mappings->loadMappings();
  62. addInput(&InputKeyboard::Get());
  63. addInput(new InputSocket(this));
  64. addInput(new InputRoku(this));
  65. #ifdef Q_OS_MAC
  66. addInput(new InputAppleRemote(this));
  67. addInput(new InputAppleMediaKeys(this));
  68. #endif
  69. #ifdef HAVE_SDL
  70. addInput(new InputSDL(this));
  71. #endif
  72. #ifdef HAVE_LIRC
  73. addInput(new InputLIRC(this));
  74. #endif
  75. #ifdef HAVE_CEC
  76. if (SettingsComponent::Get().value(SETTINGS_SECTION_CEC, "enable").toBool())
  77. addInput(new InputCEC(this));
  78. #endif
  79. return true;
  80. }
  81. /////////////////////////////////////////////////////////////////////////////////////////
  82. void InputComponent::handleAction(const QString& action, bool autoRepeat)
  83. {
  84. if (action.startsWith("host:"))
  85. {
  86. QStringList argList = action.mid(5).split(" ");
  87. QString hostCommand = argList.value(0);
  88. QString hostArguments;
  89. if (argList.size() > 1)
  90. {
  91. argList.pop_front();
  92. hostArguments = argList.join(" ");
  93. }
  94. QLOG_DEBUG() << "Got host command:" << hostCommand << "arguments:" << hostArguments;
  95. if (m_hostCommands.contains(hostCommand))
  96. {
  97. ReceiverSlot* recvSlot = m_hostCommands.value(hostCommand);
  98. if (recvSlot)
  99. {
  100. QLOG_DEBUG() << "Invoking slot" << qPrintable(recvSlot->m_slot.data());
  101. QGenericArgument arg0 = QGenericArgument();
  102. if (recvSlot->m_hasArguments)
  103. arg0 = Q_ARG(const QString&, hostArguments);
  104. QMetaObject::invokeMethod(recvSlot->m_receiver, recvSlot->m_slot.data(),
  105. Qt::AutoConnection, arg0);
  106. }
  107. }
  108. else
  109. {
  110. QLOG_WARN() << "No such host command:" << hostCommand;
  111. }
  112. }
  113. else
  114. {
  115. m_currentAction = action;
  116. emit receivedAction(action);
  117. if (autoRepeat)
  118. m_autoRepeatTimer->start(500);
  119. }
  120. }
  121. ///////////////////////////////////////////////////////////////////////////////////////////////////
  122. void InputComponent::remapInput(const QString &source, const QString &keycode, bool pressDown)
  123. {
  124. QLOG_DEBUG() << "Input received: source:" << source << "keycode:" << keycode << "pressed:" << (pressDown ? "down" : "release");
  125. if (!pressDown)
  126. {
  127. m_autoRepeatTimer->stop();
  128. m_currentAction.clear();
  129. m_currentActionCount = 0;
  130. if (!m_currentLongPressAction.isEmpty())
  131. {
  132. if (m_longHoldTimer.elapsed() > LONG_HOLD_MSEC)
  133. handleAction(m_currentLongPressAction.value("long").toString(), false);
  134. else
  135. handleAction(m_currentLongPressAction.value("short").toString(), false);
  136. m_currentLongPressAction.clear();
  137. }
  138. return;
  139. }
  140. // hide mouse if it's visible.
  141. SystemComponent::Get().setCursorVisibility(false);
  142. QVariant action = m_mappings->mapToAction(source, keycode);
  143. if (action.isNull())
  144. {
  145. QLOG_WARN() << "Could not map:" << source << keycode << "to any useful action";
  146. return;
  147. }
  148. if (action.type() == QVariant::String)
  149. {
  150. handleAction(action.toString());
  151. }
  152. else if (action.type() == QVariant::Map)
  153. {
  154. QVariantMap map = action.toMap();
  155. if (map.contains("long"))
  156. {
  157. m_longHoldTimer.start();
  158. m_currentLongPressAction = map;
  159. }
  160. else if (map.contains("short"))
  161. {
  162. handleAction(map.value("short").toString());
  163. }
  164. }
  165. }
  166. /////////////////////////////////////////////////////////////////////////////////////////
  167. void InputComponent::registerHostCommand(const QString& command, QObject* receiver, const char* slot)
  168. {
  169. auto recvSlot = new ReceiverSlot;
  170. recvSlot->m_receiver = receiver;
  171. recvSlot->m_slot = QMetaObject::normalizedSignature(slot);
  172. recvSlot->m_hasArguments = false;
  173. QLOG_DEBUG() << "Adding host command:" << qPrintable(command) << "mapped to"
  174. << qPrintable(QString(receiver->metaObject()->className()) + "::" + recvSlot->m_slot);
  175. m_hostCommands.insert(command, recvSlot);
  176. auto slotWithArgs = QString("%1(QString)").arg(QString::fromLatin1(recvSlot->m_slot)).toLatin1();
  177. auto slotWithoutArgs = QString("%1()").arg(QString::fromLatin1(recvSlot->m_slot)).toLatin1();
  178. if (recvSlot->m_receiver->metaObject()->indexOfMethod(slotWithArgs.data()) != -1)
  179. {
  180. QLOG_DEBUG() << "Host command maps to method with an argument.";
  181. recvSlot->m_hasArguments = true;
  182. }
  183. else if (recvSlot->m_receiver->metaObject()->indexOfMethod(slotWithoutArgs.data()) != -1)
  184. {
  185. QLOG_DEBUG() << "Host command maps to method without arguments.";
  186. }
  187. else
  188. {
  189. QLOG_ERROR() << "Slot for host command missing, or has incorrect signature!";
  190. }
  191. }