diff --git a/src/blackcore/blackcore.pro b/src/blackcore/blackcore.pro index f426f0817..a2e92fe33 100644 --- a/src/blackcore/blackcore.pro +++ b/src/blackcore/blackcore.pro @@ -40,6 +40,10 @@ SOURCES += *.cpp HEADERS += $$PWD/fsx/*.h SOURCES += $$PWD/fsx/*.cpp +win32 { + HEADERS += $$PWD/win/*.h + SOURCES += $$PWD/win/*.cpp +} win32:!win32-g++*: PRE_TARGETDEPS += ../../lib/blackmisc.lib ../../lib/blacksound.lib else: PRE_TARGETDEPS += ../../lib/libblackmisc.a ../../lib/libblacksound.a diff --git a/src/blackcore/win/keyboard_windows.cpp b/src/blackcore/win/keyboard_windows.cpp new file mode 100644 index 000000000..c86670f8b --- /dev/null +++ b/src/blackcore/win/keyboard_windows.cpp @@ -0,0 +1,185 @@ +/* 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); + } +} diff --git a/src/blackcore/win/keyboard_windows.h b/src/blackcore/win/keyboard_windows.h new file mode 100644 index 000000000..cb44de129 --- /dev/null +++ b/src/blackcore/win/keyboard_windows.h @@ -0,0 +1,107 @@ +/* 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/. */ + +/*! + \file +*/ + +#ifndef BLACKCORE_KEYBOARD_WINDOWS_H +#define BLACKCORE_KEYBOARD_WINDOWS_H + +#include "blackcore/keyboard.h" +#include "blackmisc/hwkeyboardkey.h" +#include +#include + +namespace BlackCore +{ + //! \brief Windows implemenation of IKeyboard using hook procedure + //! \todo Change QHash to a CCollection object + class CKeyboardWindows : public IKeyboard + { + Q_OBJECT + public: + + //! \brief Destructor + virtual ~CKeyboardWindows(); + + //! \copydoc IKeyboard::selectKey() + virtual void startCapture(bool ignoreNextKey = false) override; + + //! \copydoc IKeyboard::sizeOfRegisteredFunctions() + virtual int sizeOfRegisteredFunctions() const override; + + //! \copydoc IKeyboard::triggerKey() + virtual void triggerKey(const BlackMisc::Hardware::CKeyboardKey key, bool isPressed) override; + + //! \brief Keyboard hook handle + HHOOK keyboardHook() const { return m_keyboardHook; } + + //! \private + void keyEvent(WPARAM vkCode, uint event); + + protected: + + friend class IKeyboard; + + //! \brief Constructor + CKeyboardWindows(QObject *parent = nullptr); + + //! \brief Copy Constructor + CKeyboardWindows(CKeyboardWindows const&); + + //! \copydoc IKeyboard::init() + virtual bool init() override; + + //! \brief Assignment operator + void operator=(CKeyboardWindows const&); + + //! \copydoc IKeyboard::registerHotKeyImpl() + virtual IKeyboard::RegistrationHandle registerHotkeyImpl(BlackMisc::Hardware::CKeyboardKey key, QObject *receiver, std::function function) override; + + //! \copydoc IKeyboard::unregisterHotkeyImpl() + virtual void unregisterHotkeyImpl(const IKeyboard::RegistrationHandle &handle) override; + + //! \copydoc IKeyboard::unregisterHotkeyImpl() + virtual void unregisterAllHotkeysImpl() override; + + private: + + /*! + * \brief Constructor + * \param keySet + * \param isFinished + */ + void sendCaptureNotification(const BlackMisc::Hardware::CKeyboardKey &key, bool isFinished); + + /*! + * \brief Calls registered functions on keyboard event + * \param keySet + * \param isPressed + */ + void callFunctionsBy(const BlackMisc::Hardware::CKeyboardKey &keySet, bool isPressed); + + void addKey(WPARAM vkcode); + void removeKey(WPARAM vkcode); + + /*! + * \brief Keyboard hook procedure + * \param nCode + * \param wParam + * \param lParam + * \return + */ + static LRESULT CALLBACK keyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + + QHash> m_registeredFunctions; //!< Registered hotkey functions + BlackMisc::Hardware::CKeyboardKey m_pressedKey; //!< Set of virtual keys pressed in the last cycle + bool m_ignoreNextKey; //!< Is true if the next key needs to be ignored + HHOOK m_keyboardHook; //!< Keyboard hook handle + Mode m_mode; //!< Operation mode + }; +} + +#endif // BLACKCORE_KEYBOARD_WINDOWS_H diff --git a/src/blackcore/win/keymapping_windows.cpp b/src/blackcore/win/keymapping_windows.cpp new file mode 100644 index 000000000..cf698cd8c --- /dev/null +++ b/src/blackcore/win/keymapping_windows.cpp @@ -0,0 +1,90 @@ +/* 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 "keymapping_windows.h" +#include +#include + +using namespace BlackMisc::Hardware; + +namespace BlackCore +{ + Qt::Key CKeyMappingWindows::convertToKey(WPARAM virtualKey) + { + switch (virtualKey) + { + case '0': return Qt::Key_0; break; + case '1': return Qt::Key_1; break; + case '2': return Qt::Key_2; break; + case '3': return Qt::Key_3; break; + case '4': return Qt::Key_4; break; + case '5': return Qt::Key_5; break; + case '6': return Qt::Key_6; break; + case '7': return Qt::Key_7; break; + case '8': return Qt::Key_8; break; + case '9': return Qt::Key_9; break; + case 'A': return Qt::Key_A; break; + case 'B': return Qt::Key_B; break; + case 'C': return Qt::Key_C; break; + case 'D': return Qt::Key_D; break; + case 'E': return Qt::Key_E; break; + case 'F': return Qt::Key_F; break; + case 'G': return Qt::Key_G; break; + case 'H': return Qt::Key_H; break; + case 'I': return Qt::Key_I; break; + case 'J': return Qt::Key_J; break; + case 'K': return Qt::Key_K; break; + case 'L': return Qt::Key_L; break; + case 'M': return Qt::Key_M; break; + case 'N': return Qt::Key_N; break; + case 'O': return Qt::Key_O; break; + case 'P': return Qt::Key_P; break; + case 'Q': return Qt::Key_Q; break; + case 'R': return Qt::Key_R; break; + case 'S': return Qt::Key_S; break; + case 'T': return Qt::Key_T; break; + case 'U': return Qt::Key_U; break; + case 'V': return Qt::Key_V; break; + case 'W': return Qt::Key_W; break; + case 'X': return Qt::Key_X; break; + case 'Y': return Qt::Key_Y; break; + case 'Z': return Qt::Key_Z; break; + default: return Qt::Key_unknown; break; + } + } + + CKeyboardKey::Modifier CKeyMappingWindows::convertToModifier(WPARAM virtualKey) + { + switch (virtualKey) + { + qDebug() << virtualKey; + case VK_LSHIFT: return CKeyboardKey::ModifierShiftLeft; break; + case VK_RSHIFT: return CKeyboardKey::ModifierShiftRight; break; + case VK_LCONTROL: return CKeyboardKey::ModifierCtrlLeft; break; + case VK_RCONTROL: return CKeyboardKey::ModifierCtrlRight; break; + case VK_LMENU: return CKeyboardKey::ModifierAltLeft; break; + case VK_RMENU: return CKeyboardKey::ModifierAltRight; break; + default: return CKeyboardKey::ModifierNone; break; + } + } + + bool CKeyMappingWindows::isModifier(WPARAM vkcode) + { + switch (vkcode) + { + case VK_LSHIFT: + case VK_RSHIFT: + case VK_LCONTROL: + case VK_RCONTROL: + case VK_LMENU: + case VK_RMENU: + case VK_LWIN: + case VK_RWIN: + return true; + default: return false; + } + } + +} // namespace BlackCore diff --git a/src/blackcore/win/keymapping_windows.h b/src/blackcore/win/keymapping_windows.h new file mode 100644 index 000000000..e1bbf16ee --- /dev/null +++ b/src/blackcore/win/keymapping_windows.h @@ -0,0 +1,42 @@ +/* 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/. */ + + +#ifndef BLACKCORE_KEYMAPPING_WINDOWS_H +#define BLACKCORE_KEYMAPPING_WINDOWS_H + +#include "blackmisc/hwkeyboardkey.h" +#include + +namespace BlackCore +{ + //! \brief This class provides methods to map between windows virtual keys and CKeyboardKey + class CKeyMappingWindows + { + public: + /*! + * \brief Converts a set of windows virtual keys to a CKeySet object + * \param virtualKey + * \return + */ + static BlackMisc::Hardware::CKeyboardKey::Modifier convertToModifier(WPARAM virtualKey); + + /*! + * \brief Convert to Qt key + * \param virtualKey + * \return + */ + static Qt::Key convertToKey(WPARAM virtualKey); + + /*! + * \brief Checks if its a modifier key + * \param vkcode + * \return + */ + static bool isModifier(WPARAM vkcode); + }; + +} // namespace BlackCore +#endif // BLACKCORE_KEYMAPPING_WINDOWS_H