/* Copyright (C) 2013 VATSIM Community / contributors * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "keyboard_windows.h" #include "keymapping_windows.h" #include using namespace BlackMisc::Hardware; namespace BlackCore { CKeyboardWindows::CKeyboardWindows(QObject *parent) : IKeyboard(parent), m_keyboardHook(nullptr), m_mode(Mode_Nominal) { } CKeyboardWindows::~CKeyboardWindows() { if (m_keyboardHook) UnhookWindowsHookEx(m_keyboardHook); } bool CKeyboardWindows::init() { m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, CKeyboardWindows::keyboardProc, GetModuleHandle(NULL), 0); return true; } void CKeyboardWindows::startCapture(bool ignoreNextKey) { m_mode = Mode_Capture; m_ignoreNextKey = ignoreNextKey; m_pressedKey.setKeyObject(CKeyboardKey()); } int CKeyboardWindows::sizeOfRegisteredFunctions() const { int size = 0; foreach (QList functions, m_registeredFunctions) { size += functions.size(); } return size; } void CKeyboardWindows::triggerKey(const CKeyboardKey key, bool isPressed) { callFunctionsBy(key, isPressed); } void CKeyboardWindows::keyEvent(WPARAM vkcode, uint event) { BlackMisc::Hardware::CKeyboardKey lastPressedKey = m_pressedKey; if (m_ignoreNextKey) { m_ignoreNextKey = false; return; } bool isFinished = false; if ((event == WM_KEYDOWN) || (event == WM_SYSKEYDOWN)) { if (CKeyMappingWindows::isModifier(vkcode)) m_pressedKey.addModifier(CKeyMappingWindows::convertToModifier(vkcode)); else { m_pressedKey.setKey(CKeyMappingWindows::convertToKey(vkcode)); m_pressedKey.setNativeVirtualKey(vkcode); } } else if ((event == WM_KEYUP) || (event == WM_SYSKEYUP) ) { if (CKeyMappingWindows::isModifier(vkcode)) m_pressedKey.removeModifier(CKeyMappingWindows::convertToModifier(vkcode)); else { m_pressedKey.setKey(Qt::Key_unknown); m_pressedKey.setNativeVirtualKey(0); } isFinished = true; } if (lastPressedKey == m_pressedKey) return; #ifdef DEBUG_KEYBOARD_WINDOWS qDebug() << "Virtual key: " << vkcode; #endif if (m_mode == Mode_Capture) { if (isFinished) { sendCaptureNotification(lastPressedKey, true); m_mode = Mode_Nominal; } else { sendCaptureNotification(m_pressedKey, false); } } else { callFunctionsBy(lastPressedKey, false); callFunctionsBy(m_pressedKey, true); } } IKeyboard::RegistrationHandle CKeyboardWindows::registerHotkeyImpl(BlackMisc::Hardware::CKeyboardKey key, QObject *receiver, std::function function) { IKeyboard::RegistrationHandle handle; // Workaround: Remove key function. Otherwise operator== will not // work when we create the key value object by pressed keys key.setFunction(BlackMisc::Hardware::CKeyboardKey::HotkeyNone); if (!key.hasModifier() && !key.hasKey()) { return handle; } if (receiver == nullptr) return handle; handle.m_key = key; handle.m_receiver = receiver; handle.function = function; QList functions = m_registeredFunctions.value(key); functions.append(handle); m_registeredFunctions.insert(key, functions); return handle; } void CKeyboardWindows::unregisterHotkeyImpl(const IKeyboard::RegistrationHandle &handle) { QList functions = m_registeredFunctions.value(handle.m_key); functions.removeAll(handle); m_registeredFunctions.insert(handle.m_key, functions); } void CKeyboardWindows::unregisterAllHotkeysImpl() { m_registeredFunctions.clear(); } void CKeyboardWindows::sendCaptureNotification(const CKeyboardKey &key, bool isFinished) { if (isFinished) emit keySelectionFinished(key); else emit keySelectionChanged(key); } void CKeyboardWindows::callFunctionsBy(const CKeyboardKey &key, bool isPressed) { QList functionHandles = m_registeredFunctions.value(key); foreach (IKeyboard::RegistrationHandle functionHandle, functionHandles) { if (functionHandle.m_receiver.isNull()) { continue; } functionHandle.function(isPressed); } } LRESULT CALLBACK CKeyboardWindows::keyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { CKeyboardWindows *keyboardWindows = qobject_cast(IKeyboard::getInstance()); if (nCode == HC_ACTION) { KBDLLHOOKSTRUCT *keyboardEvent =reinterpret_cast(lParam); WPARAM vkCode = keyboardEvent->vkCode; keyboardWindows->keyEvent(vkCode, wParam); } return CallNextHookEx(keyboardWindows->keyboardHook(), nCode, wParam, lParam); } }