/* Copyright (C) 2015 * swift project Community / Contributors * * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level * directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project, * including this file, may be copied, modified, propagated, or distributed except according to the terms * contained in the LICENSE file. */ #include "keyboardmacos.h" #include "macosinpututils.h" #include "blackmisc/logmessage.h" #include #include using namespace BlackMisc; using namespace BlackMisc::Input; namespace BlackInput { // https://developer.apple.com/documentation/iokit/1592915-anonymous?language=objc static QHash keyMapping { { kHIDUsage_Keyboard0, Key_0 }, { kHIDUsage_Keyboard0, Key_1 }, { kHIDUsage_Keyboard0, Key_2 }, { kHIDUsage_Keyboard0, Key_3 }, { kHIDUsage_Keyboard4, Key_4 }, { kHIDUsage_Keyboard5, Key_5 }, { kHIDUsage_Keyboard6, Key_6 }, { kHIDUsage_Keyboard7, Key_7 }, { kHIDUsage_Keyboard8, Key_8 }, { kHIDUsage_Keyboard9, Key_9 }, { kHIDUsage_KeyboardA, Key_A }, { kHIDUsage_KeyboardB, Key_B }, { kHIDUsage_KeyboardC, Key_C }, { kHIDUsage_KeyboardD, Key_D }, { kHIDUsage_KeyboardE, Key_E }, { kHIDUsage_KeyboardF, Key_F }, { kHIDUsage_KeyboardG, Key_G }, { kHIDUsage_KeyboardH, Key_H }, { kHIDUsage_KeyboardI, Key_I }, { kHIDUsage_KeyboardJ, Key_J }, { kHIDUsage_KeyboardK, Key_K }, { kHIDUsage_KeyboardL, Key_L }, { kHIDUsage_KeyboardM, Key_M }, { kHIDUsage_KeyboardN, Key_N }, { kHIDUsage_KeyboardO, Key_O }, { kHIDUsage_KeyboardP, Key_P }, { kHIDUsage_KeyboardQ, Key_Q }, { kHIDUsage_KeyboardR, Key_R }, { kHIDUsage_KeyboardS, Key_S }, { kHIDUsage_KeyboardT, Key_T }, { kHIDUsage_KeyboardU, Key_U }, { kHIDUsage_KeyboardV, Key_V }, { kHIDUsage_KeyboardW, Key_W }, { kHIDUsage_KeyboardX, Key_X }, { kHIDUsage_KeyboardY, Key_Y }, { kHIDUsage_KeyboardZ, Key_Z }, { kHIDUsage_KeypadPlus, Key_Plus }, { kHIDUsage_KeypadHyphen, Key_Minus }, { kHIDUsage_KeyboardHyphen, Key_Minus }, { kHIDUsage_KeyboardPeriod, Key_Period }, { kHIDUsage_KeypadAsterisk, Key_Multiply }, { kHIDUsage_KeypadSlash, Key_Divide }, { kHIDUsage_KeyboardDeleteOrBackspace, Key_Back }, { kHIDUsage_KeyboardTab, Key_Tab }, { kHIDUsage_KeyboardEscape, Key_Esc }, { kHIDUsage_KeyboardSpacebar, Key_Space }, { kHIDUsage_KeyboardGraveAccentAndTilde, Key_DeadGrave }, { kHIDUsage_KeyboardComma, Key_Comma }, { kHIDUsage_Keypad0, Key_Numpad0 }, { kHIDUsage_Keypad1, Key_Numpad1 }, { kHIDUsage_Keypad2, Key_Numpad2 }, { kHIDUsage_Keypad3, Key_Numpad3 }, { kHIDUsage_Keypad4, Key_Numpad4 }, { kHIDUsage_Keypad5, Key_Numpad5 }, { kHIDUsage_Keypad6, Key_Numpad6 }, { kHIDUsage_Keypad7, Key_Numpad7 }, { kHIDUsage_Keypad8, Key_Numpad8 }, { kHIDUsage_Keypad9, Key_Numpad9 }, { kHIDUsage_KeyboardRightControl, Key_ControlRight }, { kHIDUsage_KeyboardLeftControl, Key_ControlLeft }, { kHIDUsage_KeyboardRightAlt, Key_AltRight }, { kHIDUsage_KeyboardLeftAlt, Key_AltLeft } /** fixme Missing ones Key_Insert, Key_Delete, Key_Esc = 27, Key_Space = 32, CKeyboardKey(Key_NumpadEqual), CKeyboardKey(Key_OEM1), CKeyboardKey(Key_OEM2), CKeyboardKey(Key_OEM3), CKeyboardKey(Key_OEM4), CKeyboardKey(Key_OEM5), CKeyboardKey(Key_OEM6), CKeyboardKey(Key_OEM7), CKeyboardKey(Key_OEM8), CKeyboardKey(Key_OEM102) **/ }; CKeyboardMacOS::CKeyboardMacOS(QObject *parent) : IKeyboard(parent) { } void CKeyboardMacOS::processKeyEvent(IOHIDValueRef value) { IOHIDElementRef element = IOHIDValueGetElement(value); UInt32 usagePage = IOHIDElementGetUsagePage(element); if (usagePage != kHIDPage_KeyboardOrKeypad) { return; } quint32 scancode = IOHIDElementGetUsage(element); KeyCode code = convertToKey(scancode); if (code != Key_Unknown) { CHotkeyCombination oldCombination(m_keyCombination); bool pressed = IOHIDValueGetIntegerValue(value) == 1; if (pressed) { m_keyCombination.addKeyboardKey(code); } else { m_keyCombination.removeKeyboardKey(code); } if (oldCombination != m_keyCombination) { emit keyCombinationChanged(m_keyCombination); } } } CKeyboardMacOS::~CKeyboardMacOS() { if (m_hidManager) { IOHIDManagerClose(m_hidManager, kIOHIDOptionsTypeNone); CFRelease(m_hidManager); } } bool CKeyboardMacOS::init() { if (!CMacOSInputUtils::requestAccess()) { CLogMessage(this).error(u"Access denied for keyboard input monitoring"); } m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); CFMutableArrayRef matchingArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!matchingArray) { CLogMessage(this).warning(u"Cocoa: Failed to create array"); return false; } CFDictionaryRef matchingDict = CMacOSInputUtils::createDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard); if (matchingDict) { CFArrayAppendValue(matchingArray, matchingDict); CFRelease(matchingDict); } matchingDict = CMacOSInputUtils::createDeviceMatchingDictionary(kHIDPage_GenericDesktop, kHIDUsage_GD_Keypad); if (matchingDict) { CFArrayAppendValue(matchingArray, matchingDict); CFRelease(matchingDict); } IOHIDManagerSetDeviceMatchingMultiple(m_hidManager, matchingArray); CFRelease(matchingArray); IOHIDManagerRegisterInputValueCallback(m_hidManager, valueCallback, this); IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); const auto result = IOHIDManagerOpen(m_hidManager, kIOHIDOptionsTypeNone); if (result == kIOReturnSuccess) { CLogMessage(this).debug(u"Initialized"); } else { CLogMessage(this).error(u"Failed to open HID manager for keyboard monitoring"); } return true; } void CKeyboardMacOS::valueCallback(void *context, IOReturn result, void *sender, IOHIDValueRef value) { Q_UNUSED(result); Q_UNUSED(sender); CKeyboardMacOS *obj = static_cast(context); obj->processKeyEvent(value); } KeyCode CKeyboardMacOS::convertToKey(quint32 keyCode) { return keyMapping.value(keyCode, Key_Unknown); } }