qhotkey.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. #include "qhotkey.h"
  2. #include "qhotkey_p.h"
  3. #include <QAbstractEventDispatcher>
  4. #include <QCoreApplication>
  5. #include <QDebug>
  6. #include <QMetaMethod>
  7. #include <QThread>
  8. Q_LOGGING_CATEGORY(logQHotkey, "QHotkey")
  9. void QHotkey::addGlobalMapping(const QKeySequence& shortcut,
  10. QHotkey::NativeShortcut nativeShortcut)
  11. {
  12. QMetaObject::invokeMethod(
  13. QHotkeyPrivate::instance(),
  14. "addMappingInvoked",
  15. Qt::QueuedConnection,
  16. Q_ARG(Qt::Key, Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask)),
  17. Q_ARG(Qt::KeyboardModifiers,
  18. Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask)),
  19. Q_ARG(QHotkey::NativeShortcut, nativeShortcut));
  20. }
  21. bool QHotkey::isPlatformSupported()
  22. {
  23. return QHotkeyPrivate::isPlatformSupported();
  24. }
  25. QHotkey::QHotkey(QObject* parent)
  26. : QObject(parent)
  27. , _keyCode(Qt::Key_unknown)
  28. , _modifiers(Qt::NoModifier)
  29. , _registered(false)
  30. {}
  31. QHotkey::QHotkey(const QKeySequence& shortcut,
  32. bool autoRegister,
  33. QObject* parent)
  34. : QHotkey(parent)
  35. {
  36. setShortcut(shortcut, autoRegister);
  37. }
  38. QHotkey::QHotkey(Qt::Key keyCode,
  39. Qt::KeyboardModifiers modifiers,
  40. bool autoRegister,
  41. QObject* parent)
  42. : QHotkey(parent)
  43. {
  44. setShortcut(keyCode, modifiers, autoRegister);
  45. }
  46. QHotkey::QHotkey(QHotkey::NativeShortcut shortcut,
  47. bool autoRegister,
  48. QObject* parent)
  49. : QHotkey(parent)
  50. {
  51. setNativeShortcut(shortcut, autoRegister);
  52. }
  53. QHotkey::~QHotkey()
  54. {
  55. if (_registered)
  56. QHotkeyPrivate::instance()->removeShortcut(this);
  57. }
  58. QKeySequence QHotkey::shortcut() const
  59. {
  60. if (_keyCode == Qt::Key_unknown)
  61. return QKeySequence();
  62. return QKeySequence(static_cast<int>(_keyCode | _modifiers));
  63. }
  64. Qt::Key QHotkey::keyCode() const
  65. {
  66. return _keyCode;
  67. }
  68. Qt::KeyboardModifiers QHotkey::modifiers() const
  69. {
  70. return _modifiers;
  71. }
  72. QHotkey::NativeShortcut QHotkey::currentNativeShortcut() const
  73. {
  74. return _nativeShortcut;
  75. }
  76. bool QHotkey::isRegistered() const
  77. {
  78. return _registered;
  79. }
  80. bool QHotkey::setShortcut(const QKeySequence& shortcut, bool autoRegister)
  81. {
  82. if (shortcut.isEmpty())
  83. return resetShortcut();
  84. if (shortcut.count() > 1) {
  85. qCWarning(logQHotkey,
  86. "Keysequences with multiple shortcuts are not allowed! "
  87. "Only the first shortcut will be used!");
  88. }
  89. return setShortcut(
  90. Qt::Key(shortcut[0] & ~Qt::KeyboardModifierMask),
  91. Qt::KeyboardModifiers(shortcut[0] & Qt::KeyboardModifierMask),
  92. autoRegister);
  93. }
  94. bool QHotkey::setShortcut(Qt::Key keyCode,
  95. Qt::KeyboardModifiers modifiers,
  96. bool autoRegister)
  97. {
  98. if (_registered) {
  99. if (autoRegister) {
  100. if (!QHotkeyPrivate::instance()->removeShortcut(this))
  101. return false;
  102. } else
  103. return false;
  104. }
  105. if (keyCode == Qt::Key_unknown) {
  106. _keyCode = Qt::Key_unknown;
  107. _modifiers = Qt::NoModifier;
  108. _nativeShortcut = NativeShortcut();
  109. return true;
  110. }
  111. _keyCode = keyCode;
  112. _modifiers = modifiers;
  113. _nativeShortcut =
  114. QHotkeyPrivate::instance()->nativeShortcut(keyCode, modifiers);
  115. if (_nativeShortcut.isValid()) {
  116. if (autoRegister)
  117. return QHotkeyPrivate::instance()->addShortcut(this);
  118. return true;
  119. }
  120. qCWarning(logQHotkey) << "Unable to map shortcut to native keys. Key:"
  121. << keyCode << "Modifiers:" << modifiers;
  122. _keyCode = Qt::Key_unknown;
  123. _modifiers = Qt::NoModifier;
  124. _nativeShortcut = NativeShortcut();
  125. return false;
  126. }
  127. bool QHotkey::resetShortcut()
  128. {
  129. if (_registered && !QHotkeyPrivate::instance()->removeShortcut(this)) {
  130. return false;
  131. }
  132. _keyCode = Qt::Key_unknown;
  133. _modifiers = Qt::NoModifier;
  134. _nativeShortcut = NativeShortcut();
  135. return true;
  136. }
  137. bool QHotkey::setNativeShortcut(QHotkey::NativeShortcut nativeShortcut,
  138. bool autoRegister)
  139. {
  140. if (_registered) {
  141. if (autoRegister) {
  142. if (!QHotkeyPrivate::instance()->removeShortcut(this))
  143. return false;
  144. } else
  145. return false;
  146. }
  147. if (nativeShortcut.isValid()) {
  148. _keyCode = Qt::Key_unknown;
  149. _modifiers = Qt::NoModifier;
  150. _nativeShortcut = nativeShortcut;
  151. if (autoRegister)
  152. return QHotkeyPrivate::instance()->addShortcut(this);
  153. return true;
  154. }
  155. _keyCode = Qt::Key_unknown;
  156. _modifiers = Qt::NoModifier;
  157. _nativeShortcut = NativeShortcut();
  158. return true;
  159. }
  160. bool QHotkey::setRegistered(bool registered)
  161. {
  162. if (_registered && !registered)
  163. return QHotkeyPrivate::instance()->removeShortcut(this);
  164. if (!_registered && registered) {
  165. if (!_nativeShortcut.isValid())
  166. return false;
  167. return QHotkeyPrivate::instance()->addShortcut(this);
  168. }
  169. return true;
  170. }
  171. // ---------- QHotkeyPrivate implementation ----------
  172. QHotkeyPrivate::QHotkeyPrivate()
  173. {
  174. Q_ASSERT_X(qApp,
  175. Q_FUNC_INFO,
  176. "QHotkey requires QCoreApplication to be instantiated");
  177. qApp->eventDispatcher()->installNativeEventFilter(this);
  178. }
  179. QHotkeyPrivate::~QHotkeyPrivate()
  180. {
  181. if (!shortcuts.isEmpty())
  182. qCWarning(logQHotkey)
  183. << "QHotkeyPrivate destroyed with registered shortcuts!";
  184. if (qApp && qApp->eventDispatcher())
  185. qApp->eventDispatcher()->removeNativeEventFilter(this);
  186. }
  187. QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcut(
  188. Qt::Key keycode,
  189. Qt::KeyboardModifiers modifiers)
  190. {
  191. Qt::ConnectionType conType =
  192. (QThread::currentThread() == thread() ? Qt::DirectConnection
  193. : Qt::BlockingQueuedConnection);
  194. QHotkey::NativeShortcut res;
  195. if (!QMetaObject::invokeMethod(this,
  196. "nativeShortcutInvoked",
  197. conType,
  198. Q_RETURN_ARG(QHotkey::NativeShortcut, res),
  199. Q_ARG(Qt::Key, keycode),
  200. Q_ARG(Qt::KeyboardModifiers, modifiers))) {
  201. return QHotkey::NativeShortcut();
  202. }
  203. return res;
  204. }
  205. bool QHotkeyPrivate::addShortcut(QHotkey* hotkey)
  206. {
  207. if (hotkey->_registered)
  208. return false;
  209. Qt::ConnectionType conType =
  210. (QThread::currentThread() == thread() ? Qt::DirectConnection
  211. : Qt::BlockingQueuedConnection);
  212. bool res = false;
  213. if (!QMetaObject::invokeMethod(this,
  214. "addShortcutInvoked",
  215. conType,
  216. Q_RETURN_ARG(bool, res),
  217. Q_ARG(QHotkey*, hotkey))) {
  218. return false;
  219. }
  220. if (res)
  221. emit hotkey->registeredChanged(true);
  222. return res;
  223. }
  224. bool QHotkeyPrivate::removeShortcut(QHotkey* hotkey)
  225. {
  226. if (!hotkey->_registered)
  227. return false;
  228. Qt::ConnectionType conType =
  229. (QThread::currentThread() == thread() ? Qt::DirectConnection
  230. : Qt::BlockingQueuedConnection);
  231. bool res = false;
  232. if (!QMetaObject::invokeMethod(this,
  233. "removeShortcutInvoked",
  234. conType,
  235. Q_RETURN_ARG(bool, res),
  236. Q_ARG(QHotkey*, hotkey))) {
  237. return false;
  238. }
  239. if (res)
  240. emit hotkey->registeredChanged(false);
  241. return res;
  242. }
  243. void QHotkeyPrivate::activateShortcut(QHotkey::NativeShortcut shortcut)
  244. {
  245. QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::activated);
  246. for (QHotkey* hkey : shortcuts.values(shortcut))
  247. signal.invoke(hkey, Qt::QueuedConnection);
  248. }
  249. void QHotkeyPrivate::releaseShortcut(QHotkey::NativeShortcut shortcut)
  250. {
  251. QMetaMethod signal = QMetaMethod::fromSignal(&QHotkey::released);
  252. for (QHotkey* hkey : shortcuts.values(shortcut))
  253. signal.invoke(hkey, Qt::QueuedConnection);
  254. }
  255. void QHotkeyPrivate::addMappingInvoked(Qt::Key keycode,
  256. Qt::KeyboardModifiers modifiers,
  257. QHotkey::NativeShortcut nativeShortcut)
  258. {
  259. mapping.insert({ keycode, modifiers }, nativeShortcut);
  260. }
  261. bool QHotkeyPrivate::addShortcutInvoked(QHotkey* hotkey)
  262. {
  263. QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
  264. if (!shortcuts.contains(shortcut)) {
  265. if (!registerShortcut(shortcut)) {
  266. qCWarning(logQHotkey)
  267. << QHotkey::tr("Failed to register %1. Error: %2")
  268. .arg(hotkey->shortcut().toString(), error);
  269. return false;
  270. }
  271. }
  272. shortcuts.insert(shortcut, hotkey);
  273. hotkey->_registered = true;
  274. return true;
  275. }
  276. bool QHotkeyPrivate::removeShortcutInvoked(QHotkey* hotkey)
  277. {
  278. QHotkey::NativeShortcut shortcut = hotkey->_nativeShortcut;
  279. if (shortcuts.remove(shortcut, hotkey) == 0)
  280. return false;
  281. hotkey->_registered = false;
  282. emit hotkey->registeredChanged(true);
  283. if (shortcuts.count(shortcut) == 0) {
  284. if (!unregisterShortcut(shortcut)) {
  285. qCWarning(logQHotkey)
  286. << QHotkey::tr("Failed to unregister %1. Error: %2")
  287. .arg(hotkey->shortcut().toString(), error);
  288. return false;
  289. }
  290. return true;
  291. }
  292. return true;
  293. }
  294. QHotkey::NativeShortcut QHotkeyPrivate::nativeShortcutInvoked(
  295. Qt::Key keycode,
  296. Qt::KeyboardModifiers modifiers)
  297. {
  298. if (mapping.contains({ keycode, modifiers }))
  299. return mapping.value({ keycode, modifiers });
  300. bool ok1 = false;
  301. auto k = nativeKeycode(keycode, ok1);
  302. bool ok2 = false;
  303. auto m = nativeModifiers(modifiers, ok2);
  304. if (ok1 && ok2)
  305. return { k, m };
  306. return {};
  307. }
  308. QHotkey::NativeShortcut::NativeShortcut()
  309. : key()
  310. , modifier()
  311. , valid(false)
  312. {}
  313. QHotkey::NativeShortcut::NativeShortcut(quint32 key, quint32 modifier)
  314. : key(key)
  315. , modifier(modifier)
  316. , valid(true)
  317. {}
  318. bool QHotkey::NativeShortcut::isValid() const
  319. {
  320. return valid;
  321. }
  322. bool QHotkey::NativeShortcut::operator==(QHotkey::NativeShortcut other) const
  323. {
  324. return (key == other.key) && (modifier == other.modifier) &&
  325. valid == other.valid;
  326. }
  327. bool QHotkey::NativeShortcut::operator!=(QHotkey::NativeShortcut other) const
  328. {
  329. return (key != other.key) || (modifier != other.modifier) ||
  330. valid != other.valid;
  331. }
  332. uint qHash(QHotkey::NativeShortcut key)
  333. {
  334. return qHash(key.key) ^ qHash(key.modifier);
  335. }
  336. uint qHash(QHotkey::NativeShortcut key, uint seed)
  337. {
  338. return qHash(key.key, seed) ^ qHash(key.modifier, seed);
  339. }