swift
keyboardmacos.mm
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
4 #include "keyboardmacos.h"
5 #include "macosinpututils.h"
6 #include "misc/logmessage.h"
7 
8 #include <CoreFoundation/CoreFoundation.h>
9 #include <array>
10 
11 using namespace swift::misc;
12 using namespace swift::misc::input;
13 
14 namespace swift::input
15 {
16  // https://developer.apple.com/documentation/iokit/1592915-anonymous?language=objc
17  static QHash<quint32, KeyCode> keyMapping
18  {
19  { kHIDUsage_Keyboard0, Key_0 },
20  { kHIDUsage_Keyboard1, Key_1 },
21  { kHIDUsage_Keyboard2, Key_2 },
22  { kHIDUsage_Keyboard3, Key_3 },
23  { kHIDUsage_Keyboard4, Key_4 },
24  { kHIDUsage_Keyboard5, Key_5 },
25  { kHIDUsage_Keyboard6, Key_6 },
26  { kHIDUsage_Keyboard7, Key_7 },
27  { kHIDUsage_Keyboard8, Key_8 },
28  { kHIDUsage_Keyboard9, Key_9 },
29  { kHIDUsage_KeyboardA, Key_A },
30  { kHIDUsage_KeyboardB, Key_B },
31  { kHIDUsage_KeyboardC, Key_C },
32  { kHIDUsage_KeyboardD, Key_D },
33  { kHIDUsage_KeyboardE, Key_E },
34  { kHIDUsage_KeyboardF, Key_F },
35  { kHIDUsage_KeyboardG, Key_G },
36  { kHIDUsage_KeyboardH, Key_H },
37  { kHIDUsage_KeyboardI, Key_I },
38  { kHIDUsage_KeyboardJ, Key_J },
39  { kHIDUsage_KeyboardK, Key_K },
40  { kHIDUsage_KeyboardL, Key_L },
41  { kHIDUsage_KeyboardM, Key_M },
42  { kHIDUsage_KeyboardN, Key_N },
43  { kHIDUsage_KeyboardO, Key_O },
44  { kHIDUsage_KeyboardP, Key_P },
45  { kHIDUsage_KeyboardQ, Key_Q },
46  { kHIDUsage_KeyboardR, Key_R },
47  { kHIDUsage_KeyboardS, Key_S },
48  { kHIDUsage_KeyboardT, Key_T },
49  { kHIDUsage_KeyboardU, Key_U },
50  { kHIDUsage_KeyboardV, Key_V },
51  { kHIDUsage_KeyboardW, Key_W },
52  { kHIDUsage_KeyboardX, Key_X },
53  { kHIDUsage_KeyboardY, Key_Y },
54  { kHIDUsage_KeyboardZ, Key_Z },
55  { kHIDUsage_KeypadPlus, Key_NumpadPlus },
56  { kHIDUsage_KeypadHyphen, Key_NumpadMinus },
57  { kHIDUsage_KeyboardHyphen, Key_Minus },
58  { kHIDUsage_KeyboardPeriod, Key_Period },
59  { kHIDUsage_KeypadAsterisk, Key_Multiply },
60  { kHIDUsage_KeypadSlash, Key_Divide },
61  { kHIDUsage_KeypadHyphen, Key_NumpadMinus },
62  { kHIDUsage_KeypadPlus, Key_NumpadPlus },
63  { kHIDUsage_KeypadPeriod, Key_NumpadDelete },
64  { kHIDUsage_KeyboardDeleteOrBackspace, Key_Back },
65  { kHIDUsage_KeyboardTab, Key_Tab },
66  { kHIDUsage_KeyboardEscape, Key_Esc },
67  { kHIDUsage_KeyboardSpacebar, Key_Space },
68  { kHIDUsage_KeyboardGraveAccentAndTilde, Key_DeadGrave },
69  { kHIDUsage_KeyboardComma, Key_Comma },
70  { kHIDUsage_Keypad0, Key_Numpad0 },
71  { kHIDUsage_Keypad1, Key_Numpad1 },
72  { kHIDUsage_Keypad2, Key_Numpad2 },
73  { kHIDUsage_Keypad3, Key_Numpad3 },
74  { kHIDUsage_Keypad4, Key_Numpad4 },
75  { kHIDUsage_Keypad5, Key_Numpad5 },
76  { kHIDUsage_Keypad6, Key_Numpad6 },
77  { kHIDUsage_Keypad7, Key_Numpad7 },
78  { kHIDUsage_Keypad8, Key_Numpad8 },
79  { kHIDUsage_Keypad9, Key_Numpad9 },
80  { kHIDUsage_KeyboardRightControl, Key_ControlRight },
81  { kHIDUsage_KeyboardLeftControl, Key_ControlLeft },
82  { kHIDUsage_KeyboardRightAlt, Key_AltRight },
83  { kHIDUsage_KeyboardLeftAlt, Key_AltLeft },
84  { kHIDUsage_KeyboardInsert, Key_Insert },
85  { kHIDUsage_KeyboardDeleteForward, Key_Delete },
86  { kHIDUsage_KeyboardHome, Key_Home },
87  { kHIDUsage_KeyboardEnd, Key_End },
88  { kHIDUsage_KeyboardPageUp, Key_PageUp },
89  { kHIDUsage_KeyboardPageDown, Key_PageDown },
90  { kHIDUsage_KeyboardCapsLock, Key_CapsLock },
91  { kHIDUsage_KeyboardReturn, Key_Enter },
92  { kHIDUsage_KeyboardF1, Key_Function1 },
93  { kHIDUsage_KeyboardF2, Key_Function2 },
94  { kHIDUsage_KeyboardF3, Key_Function3 },
95  { kHIDUsage_KeyboardF4, Key_Function4 },
96  { kHIDUsage_KeyboardF5, Key_Function5 },
97  { kHIDUsage_KeyboardF6, Key_Function6 },
98  { kHIDUsage_KeyboardF7, Key_Function7 },
99  { kHIDUsage_KeyboardF8, Key_Function8 },
100  { kHIDUsage_KeyboardF9, Key_Function9 },
101  { kHIDUsage_KeyboardF10, Key_Function10 },
102  { kHIDUsage_KeyboardF11, Key_Function11 },
103  { kHIDUsage_KeyboardF12, Key_Function12 },
104  { kHIDUsage_KeyboardF13, Key_Function13 },
105  { kHIDUsage_KeyboardF14, Key_Function14 },
106  { kHIDUsage_KeyboardF15, Key_Function15 },
107  { kHIDUsage_KeyboardF16, Key_Function16 },
108  { kHIDUsage_KeyboardF17, Key_Function17 },
109  { kHIDUsage_KeyboardF18, Key_Function18 },
110  { kHIDUsage_KeyboardF19, Key_Function19 },
111  { kHIDUsage_KeyboardF20, Key_Function20 },
112  { kHIDUsage_KeyboardF21, Key_Function21 },
113  { kHIDUsage_KeyboardF22, Key_Function22 },
114  { kHIDUsage_KeyboardF23, Key_Function23 },
115  { kHIDUsage_KeyboardF24, Key_Function24 },
116 
129  };
130 
131  CKeyboardMacOS::CKeyboardMacOS(QObject *parent) : IKeyboard(parent)
132  { }
133 
134  void CKeyboardMacOS::processKeyEvent(IOHIDValueRef value)
135  {
136  IOHIDElementRef element = IOHIDValueGetElement(value);
137  UInt32 usagePage = IOHIDElementGetUsagePage(element);
138  if (usagePage != kHIDPage_KeyboardOrKeypad) { return; }
139 
140  quint32 scancode = IOHIDElementGetUsage(element);
141 
142  KeyCode code = convertToKey(scancode);
143  if (code != Key_Unknown)
144  {
145  CHotkeyCombination oldCombination(m_keyCombination);
146  bool pressed = IOHIDValueGetIntegerValue(value) == 1;
147  if (pressed)
148  {
149  m_keyCombination.addKeyboardKey(code);
150  }
151  else
152  {
153  m_keyCombination.removeKeyboardKey(code);
154  }
155 
156  if (oldCombination != m_keyCombination)
157  {
158  emit keyCombinationChanged(m_keyCombination);
159  }
160  }
161  }
162 
163  CKeyboardMacOS::~CKeyboardMacOS()
164  {
165  if (m_hidManager)
166  {
167  IOHIDManagerClose(m_hidManager, kIOHIDOptionsTypeNone);
168  CFRelease(m_hidManager);
169  }
170  }
171 
172  bool CKeyboardMacOS::init()
173  {
174  if (!CMacOSInputUtils::requestAccess())
175  {
176  CLogMessage(this).error(u"Access denied for keyboard input monitoring");
177  }
178  m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
179 
180  CFMutableArrayRef matchingArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
181  if (!matchingArray)
182  {
183  CLogMessage(this).warning(u"Cocoa: Failed to create array");
184  return false;
185  }
186 
187  CFDictionaryRef matchingDict = CMacOSInputUtils::createDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
188  if (matchingDict)
189  {
190  CFArrayAppendValue(matchingArray, matchingDict);
191  CFRelease(matchingDict);
192  }
193 
194  matchingDict = CMacOSInputUtils::createDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad);
195  if (matchingDict)
196  {
197  CFArrayAppendValue(matchingArray, matchingDict);
198  CFRelease(matchingDict);
199  }
200 
201  IOHIDManagerSetDeviceMatchingMultiple(m_hidManager, matchingArray);
202  CFRelease(matchingArray);
203 
204  IOHIDManagerRegisterInputValueCallback(m_hidManager, valueCallback, this);
205  IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
206  const auto result = IOHIDManagerOpen(m_hidManager, kIOHIDOptionsTypeNone);
207 
208  if (result == kIOReturnSuccess)
209  {
210  CLogMessage(this).debug(u"Initialized");
211  }
212  else
213  {
214  CLogMessage(this).error(u"Failed to open HID manager for keyboard monitoring");
215  }
216  return true;
217  }
218 
219  void CKeyboardMacOS::valueCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value)
220  {
221  Q_UNUSED(result);
222  Q_UNUSED(sender);
223  CKeyboardMacOS *obj = static_cast<CKeyboardMacOS *>(context);
224  obj->processKeyEvent(value);
225  }
226 
227  KeyCode CKeyboardMacOS::convertToKey(quint32 keyCode)
228  {
229  return keyMapping.value(keyCode, Key_Unknown);
230  }
231 }
MacOS implemenation of IKeyboard using hook procedure.
Definition: keyboardmacos.h:22
Class for emitting a log message.
Definition: logmessage.h:27
Derived & warning(const char16_t(&format)[N])
Set the severity to warning, providing a format string.
Derived & error(const char16_t(&format)[N])
Set the severity to error, providing a format string.
Derived & debug()
Set the severity to debug.
Value object representing hotkey sequence.
KeyCode
Key code http://www.kbdlayout.info/.
Definition: keycodes.h:16
Free functions in swift::misc.