InputComponent.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. if (recvSlot->m_function)
  101. {
  102. QLOG_DEBUG() << "Invoking anonymous function";
  103. recvSlot->m_function();
  104. }
  105. else
  106. {
  107. QLOG_DEBUG() << "Invoking slot" << qPrintable(recvSlot->m_slot.data());
  108. QGenericArgument arg0 = QGenericArgument();
  109. if (recvSlot->m_hasArguments)
  110. arg0 = Q_ARG(const QString&, hostArguments);
  111. QMetaObject::invokeMethod(recvSlot->m_receiver, recvSlot->m_slot.data(),
  112. Qt::AutoConnection, arg0);
  113. }
  114. }
  115. }
  116. else
  117. {
  118. QLOG_WARN() << "No such host command:" << hostCommand;
  119. }
  120. }
  121. else
  122. {
  123. m_currentAction = action;
  124. emit receivedAction(action);
  125. if (autoRepeat)
  126. m_autoRepeatTimer->start(500);
  127. }
  128. }
  129. ///////////////////////////////////////////////////////////////////////////////////////////////////
  130. void InputComponent::remapInput(const QString &source, const QString &keycode, bool pressDown)
  131. {
  132. QLOG_DEBUG() << "Input received: source:" << source << "keycode:" << keycode << "pressed:" << (pressDown ? "down" : "release");
  133. emit receivedInput();
  134. if (!pressDown)
  135. {
  136. m_autoRepeatTimer->stop();
  137. m_currentAction.clear();
  138. m_currentActionCount = 0;
  139. if (!m_currentLongPressAction.isEmpty())
  140. {
  141. if (m_longHoldTimer.elapsed() > LONG_HOLD_MSEC)
  142. handleAction(m_currentLongPressAction.value("long").toString(), false);
  143. else
  144. handleAction(m_currentLongPressAction.value("short").toString(), false);
  145. m_currentLongPressAction.clear();
  146. }
  147. return;
  148. }
  149. auto actions = m_mappings->mapToAction(source, keycode);
  150. if (actions.isEmpty())
  151. {
  152. QLOG_WARN() << "Could not map:" << source << keycode << "to any useful action";
  153. return;
  154. }
  155. for (auto action : actions)
  156. {
  157. if (action.type() == QVariant::String)
  158. {
  159. handleAction(action.toString());
  160. }
  161. else if (action.type() == QVariant::Map)
  162. {
  163. QVariantMap map = action.toMap();
  164. if (map.contains("long"))
  165. {
  166. m_longHoldTimer.start();
  167. m_currentLongPressAction = map;
  168. }
  169. else if (map.contains("short"))
  170. {
  171. handleAction(map.value("short").toString());
  172. }
  173. }
  174. }
  175. }
  176. /////////////////////////////////////////////////////////////////////////////////////////
  177. void InputComponent::registerHostCommand(const QString& command, QObject* receiver, const char* slot)
  178. {
  179. auto recvSlot = new ReceiverSlot;
  180. recvSlot->m_receiver = receiver;
  181. recvSlot->m_slot = QMetaObject::normalizedSignature(slot);
  182. recvSlot->m_hasArguments = false;
  183. QLOG_DEBUG() << "Adding host command:" << qPrintable(command) << "mapped to"
  184. << qPrintable(QString(receiver->metaObject()->className()) + "::" + recvSlot->m_slot);
  185. m_hostCommands.insert(command, recvSlot);
  186. auto slotWithArgs = QString("%1(QString)").arg(QString::fromLatin1(recvSlot->m_slot)).toLatin1();
  187. auto slotWithoutArgs = QString("%1()").arg(QString::fromLatin1(recvSlot->m_slot)).toLatin1();
  188. if (recvSlot->m_receiver->metaObject()->indexOfMethod(slotWithArgs.data()) != -1)
  189. {
  190. QLOG_DEBUG() << "Host command maps to method with an argument.";
  191. recvSlot->m_hasArguments = true;
  192. }
  193. else if (recvSlot->m_receiver->metaObject()->indexOfMethod(slotWithoutArgs.data()) != -1)
  194. {
  195. QLOG_DEBUG() << "Host command maps to method without arguments.";
  196. }
  197. else
  198. {
  199. QLOG_ERROR() << "Slot for host command missing, or has incorrect signature!";
  200. }
  201. }
  202. /////////////////////////////////////////////////////////////////////////////////////////
  203. void InputComponent::registerHostCommand(const QString& command, std::function<void(void)> function)
  204. {
  205. auto recvSlot = new ReceiverSlot;
  206. recvSlot->m_function = function;
  207. QLOG_DEBUG() << "Adding host command:" << qPrintable(command) << "mapped to anonymous function";
  208. m_hostCommands.insert(command, recvSlot);
  209. }