qhotkey_mac.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #include "qhotkey.h"
  2. #include "qhotkey_p.h"
  3. #include <Carbon/Carbon.h>
  4. #include <QDebug>
  5. class QHotkeyPrivateMac : public QHotkeyPrivate
  6. {
  7. public:
  8. // QAbstractNativeEventFilter interface
  9. bool nativeEventFilter(const QByteArray& eventType,
  10. void* message,
  11. long* result) Q_DECL_OVERRIDE;
  12. static OSStatus hotkeyPressEventHandler(EventHandlerCallRef nextHandler,
  13. EventRef event,
  14. void* data);
  15. static OSStatus hotkeyReleaseEventHandler(EventHandlerCallRef nextHandler,
  16. EventRef event,
  17. void* data);
  18. protected:
  19. // QHotkeyPrivate interface
  20. quint32 nativeKeycode(Qt::Key keycode, bool& ok) Q_DECL_OVERRIDE;
  21. quint32 nativeModifiers(Qt::KeyboardModifiers modifiers,
  22. bool& ok) Q_DECL_OVERRIDE;
  23. bool registerShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
  24. bool unregisterShortcut(QHotkey::NativeShortcut shortcut) Q_DECL_OVERRIDE;
  25. private:
  26. static bool isHotkeyHandlerRegistered;
  27. static QHash<QHotkey::NativeShortcut, EventHotKeyRef> hotkeyRefs;
  28. };
  29. NATIVE_INSTANCE(QHotkeyPrivateMac)
  30. bool QHotkeyPrivate::isPlatformSupported()
  31. {
  32. return true;
  33. }
  34. bool QHotkeyPrivateMac::isHotkeyHandlerRegistered = false;
  35. QHash<QHotkey::NativeShortcut, EventHotKeyRef> QHotkeyPrivateMac::hotkeyRefs;
  36. bool QHotkeyPrivateMac::nativeEventFilter(const QByteArray& eventType,
  37. void* message,
  38. long* result)
  39. {
  40. Q_UNUSED(eventType)
  41. Q_UNUSED(message)
  42. Q_UNUSED(result)
  43. return false;
  44. }
  45. quint32 QHotkeyPrivateMac::nativeKeycode(Qt::Key keycode, bool& ok)
  46. {
  47. // Constants found in NSEvent.h from AppKit.framework
  48. ok = true;
  49. switch (keycode) {
  50. case Qt::Key_Return:
  51. return kVK_Return;
  52. case Qt::Key_Enter:
  53. return kVK_ANSI_KeypadEnter;
  54. case Qt::Key_Tab:
  55. return kVK_Tab;
  56. case Qt::Key_Space:
  57. return kVK_Space;
  58. case Qt::Key_Backspace:
  59. return kVK_Delete;
  60. case Qt::Key_Escape:
  61. return kVK_Escape;
  62. case Qt::Key_CapsLock:
  63. return kVK_CapsLock;
  64. case Qt::Key_Option:
  65. return kVK_Option;
  66. case Qt::Key_F17:
  67. return kVK_F17;
  68. case Qt::Key_VolumeUp:
  69. return kVK_VolumeUp;
  70. case Qt::Key_VolumeDown:
  71. return kVK_VolumeDown;
  72. case Qt::Key_F18:
  73. return kVK_F18;
  74. case Qt::Key_F19:
  75. return kVK_F19;
  76. case Qt::Key_F20:
  77. return kVK_F20;
  78. case Qt::Key_F5:
  79. return kVK_F5;
  80. case Qt::Key_F6:
  81. return kVK_F6;
  82. case Qt::Key_F7:
  83. return kVK_F7;
  84. case Qt::Key_F3:
  85. return kVK_F3;
  86. case Qt::Key_F8:
  87. return kVK_F8;
  88. case Qt::Key_F9:
  89. return kVK_F9;
  90. case Qt::Key_F11:
  91. return kVK_F11;
  92. case Qt::Key_F13:
  93. return kVK_F13;
  94. case Qt::Key_F16:
  95. return kVK_F16;
  96. case Qt::Key_F14:
  97. return kVK_F14;
  98. case Qt::Key_F10:
  99. return kVK_F10;
  100. case Qt::Key_F12:
  101. return kVK_F12;
  102. case Qt::Key_F15:
  103. return kVK_F15;
  104. case Qt::Key_Help:
  105. return kVK_Help;
  106. case Qt::Key_Home:
  107. return kVK_Home;
  108. case Qt::Key_PageUp:
  109. return kVK_PageUp;
  110. case Qt::Key_Delete:
  111. return kVK_ForwardDelete;
  112. case Qt::Key_F4:
  113. return kVK_F4;
  114. case Qt::Key_End:
  115. return kVK_End;
  116. case Qt::Key_F2:
  117. return kVK_F2;
  118. case Qt::Key_PageDown:
  119. return kVK_PageDown;
  120. case Qt::Key_F1:
  121. return kVK_F1;
  122. case Qt::Key_Left:
  123. return kVK_LeftArrow;
  124. case Qt::Key_Right:
  125. return kVK_RightArrow;
  126. case Qt::Key_Down:
  127. return kVK_DownArrow;
  128. case Qt::Key_Up:
  129. return kVK_UpArrow;
  130. default:
  131. ok = false;
  132. break;
  133. }
  134. UTF16Char ch = keycode;
  135. CFDataRef currentLayoutData;
  136. TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
  137. if (currentKeyboard == NULL)
  138. return 0;
  139. currentLayoutData = (CFDataRef)TISGetInputSourceProperty(
  140. currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
  141. CFRelease(currentKeyboard);
  142. if (currentLayoutData == NULL)
  143. return 0;
  144. UCKeyboardLayout* header =
  145. (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData);
  146. UCKeyboardTypeHeader* table = header->keyboardTypeList;
  147. uint8_t* data = (uint8_t*)header;
  148. for (quint32 i = 0; i < header->keyboardTypeCount; i++) {
  149. UCKeyStateRecordsIndex* stateRec = 0;
  150. if (table[i].keyStateRecordsIndexOffset != 0) {
  151. stateRec = reinterpret_cast<UCKeyStateRecordsIndex*>(
  152. data + table[i].keyStateRecordsIndexOffset);
  153. if (stateRec->keyStateRecordsIndexFormat !=
  154. kUCKeyStateRecordsIndexFormat)
  155. stateRec = 0;
  156. }
  157. UCKeyToCharTableIndex* charTable =
  158. reinterpret_cast<UCKeyToCharTableIndex*>(
  159. data + table[i].keyToCharTableIndexOffset);
  160. if (charTable->keyToCharTableIndexFormat !=
  161. kUCKeyToCharTableIndexFormat)
  162. continue;
  163. for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) {
  164. UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(
  165. data + charTable->keyToCharTableOffsets[j]);
  166. for (quint32 k = 0; k < charTable->keyToCharTableSize; k++) {
  167. if (keyToChar[k] & kUCKeyOutputTestForIndexMask) {
  168. long idx = keyToChar[k] & kUCKeyOutputGetIndexMask;
  169. if (stateRec && idx < stateRec->keyStateRecordCount) {
  170. UCKeyStateRecord* rec =
  171. reinterpret_cast<UCKeyStateRecord*>(
  172. data + stateRec->keyStateRecordOffsets[idx]);
  173. if (rec->stateZeroCharData == ch) {
  174. ok = true;
  175. return k;
  176. }
  177. }
  178. } else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) &&
  179. keyToChar[k] < 0xFFFE) {
  180. if (keyToChar[k] == ch) {
  181. ok = true;
  182. return k;
  183. }
  184. }
  185. }
  186. }
  187. }
  188. return 0;
  189. }
  190. quint32 QHotkeyPrivateMac::nativeModifiers(Qt::KeyboardModifiers modifiers,
  191. bool& ok)
  192. {
  193. quint32 nMods = 0;
  194. if (modifiers & Qt::ShiftModifier)
  195. nMods |= shiftKey;
  196. if (modifiers & Qt::ControlModifier)
  197. nMods |= cmdKey;
  198. if (modifiers & Qt::AltModifier)
  199. nMods |= optionKey;
  200. if (modifiers & Qt::MetaModifier)
  201. nMods |= controlKey;
  202. if (modifiers & Qt::KeypadModifier)
  203. nMods |= kEventKeyModifierNumLockMask;
  204. ok = true;
  205. return nMods;
  206. }
  207. bool QHotkeyPrivateMac::registerShortcut(QHotkey::NativeShortcut shortcut)
  208. {
  209. if (!this->isHotkeyHandlerRegistered) {
  210. EventTypeSpec pressEventSpec;
  211. pressEventSpec.eventClass = kEventClassKeyboard;
  212. pressEventSpec.eventKind = kEventHotKeyPressed;
  213. InstallApplicationEventHandler(
  214. &QHotkeyPrivateMac::hotkeyPressEventHandler,
  215. 1,
  216. &pressEventSpec,
  217. NULL,
  218. NULL);
  219. EventTypeSpec releaseEventSpec;
  220. releaseEventSpec.eventClass = kEventClassKeyboard;
  221. releaseEventSpec.eventKind = kEventHotKeyReleased;
  222. InstallApplicationEventHandler(
  223. &QHotkeyPrivateMac::hotkeyReleaseEventHandler,
  224. 1,
  225. &releaseEventSpec,
  226. NULL,
  227. NULL);
  228. }
  229. EventHotKeyID hkeyID;
  230. hkeyID.signature = shortcut.key;
  231. hkeyID.id = shortcut.modifier;
  232. EventHotKeyRef eventRef = 0;
  233. OSStatus status = RegisterEventHotKey(shortcut.key,
  234. shortcut.modifier,
  235. hkeyID,
  236. GetApplicationEventTarget(),
  237. 0,
  238. &eventRef);
  239. if (status != noErr) {
  240. error = QString::number(status);
  241. return false;
  242. } else {
  243. this->hotkeyRefs.insert(shortcut, eventRef);
  244. return true;
  245. }
  246. }
  247. bool QHotkeyPrivateMac::unregisterShortcut(QHotkey::NativeShortcut shortcut)
  248. {
  249. EventHotKeyRef eventRef = QHotkeyPrivateMac::hotkeyRefs.value(shortcut);
  250. OSStatus status = UnregisterEventHotKey(eventRef);
  251. if (status != noErr) {
  252. error = QString::number(status);
  253. return false;
  254. } else {
  255. this->hotkeyRefs.remove(shortcut);
  256. return true;
  257. }
  258. }
  259. OSStatus QHotkeyPrivateMac::hotkeyPressEventHandler(
  260. EventHandlerCallRef nextHandler,
  261. EventRef event,
  262. void* data)
  263. {
  264. Q_UNUSED(nextHandler);
  265. Q_UNUSED(data);
  266. if (GetEventClass(event) == kEventClassKeyboard &&
  267. GetEventKind(event) == kEventHotKeyPressed) {
  268. EventHotKeyID hkeyID;
  269. GetEventParameter(event,
  270. kEventParamDirectObject,
  271. typeEventHotKeyID,
  272. NULL,
  273. sizeof(EventHotKeyID),
  274. NULL,
  275. &hkeyID);
  276. hotkeyPrivate->activateShortcut({ hkeyID.signature, hkeyID.id });
  277. }
  278. return noErr;
  279. }
  280. OSStatus QHotkeyPrivateMac::hotkeyReleaseEventHandler(
  281. EventHandlerCallRef nextHandler,
  282. EventRef event,
  283. void* data)
  284. {
  285. Q_UNUSED(nextHandler);
  286. Q_UNUSED(data);
  287. if (GetEventClass(event) == kEventClassKeyboard &&
  288. GetEventKind(event) == kEventHotKeyReleased) {
  289. EventHotKeyID hkeyID;
  290. GetEventParameter(event,
  291. kEventParamDirectObject,
  292. typeEventHotKeyID,
  293. NULL,
  294. sizeof(EventHotKeyID),
  295. NULL,
  296. &hkeyID);
  297. hotkeyPrivate->releaseShortcut({ hkeyID.signature, hkeyID.id });
  298. }
  299. return noErr;
  300. }