Qt实现全局键盘事件监听器-Windows

news2024/11/17 23:24:40

Qt实现全局键盘事件监听器-Windows版🍇

文章目录

  • Qt实现全局键盘事件监听器-Windows版🍇
    • 1、概述🍈
    • 2、实现效果🍉
    • 3、实现方式🍊
    • 4、关键代码🍋
    • 5、源代码🍌

更多精彩内容
👉个人内容分类汇总 👈
👉Qt自定义模块、工具👈

1、概述🍈

  • Qt版本:V5.12.5
  • 兼容系统:
    • Windows:这里测试了Windows10,其它的版本没有测试;
    • Linux:这里测试了ubuntu18.04、20.04,其它的没有测试;
    • Mac:等啥时候我有了Mac电脑再说。
  1. 有时候我们想获取到全局键盘事件,使用Qt的键盘事件、事件过滤器之类的都无法实现,因为当鼠标移出当前窗口或者当前窗口失去焦点、窗口最小化了就无法获取到键盘事件了;
  2. 而Windows下想要监听到全局键盘事件就需要使用到Windows的低级键盘钩子来实现;
  3. 关于Windows的键盘钩子API文档可以看微软官网SetWindowsHookExW ;
  4. 在这个类中通过Windows键盘钩子API监听到全局键盘事件;
  5. 然后将监听到的键盘事件映射为QKeyEvent事件,便于在Qt里面使用;
  6. 注意:由于按键状态非常多,想要将Windows键盘事件映射为QKeyEvent会比较麻烦,这里可以看Qt源码怎么实现的D:\Qt\Qt5.12.5\5.12.5\Src\qtbase\src\plugins\platforms\windows\qwindowskeymapper.cpp

2、实现效果🍉

在这里插入图片描述

3、实现方式🍊

  1. 使用SetWindowsHookExW()函数挂钩低级键盘钩子;
  2. 通过回调函数LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)监听到全局键盘事件;
  3. wParam参数表示信号类型:
    1. WM_KEYDOWN:按下非系统键, 非系统键是未按下 ALT 键时按下的键
    2. WM_KEYUP:当释放非系统键
    3. WM_SYSKEYDOWN:当用户按下 F10 键 (激活菜单栏) 或按住 Alt 键,然后按另一个键
    4. WM_SYSKEYUP:当用户释放按下 Alt 键时按下的键
  4. 使用KBDLLHOOKSTRUCT * kbdll = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam)将lParam转换为KBDLLHOOKSTRUCT结构体的指针,可通过这个结构体获取当前键盘按下的【虚拟密钥代码】、【密钥的硬件扫描代码】之类的信息;
  5. 然后将获取到的键盘事件映射为QKeyEvent事件,发送给当前程序使用;
    • 将windows键盘事件映射为QKeyEvent事件需要考虑按键基本键值、按下修饰键(Shift、Ctrl等)时的键值、大小写字母、小键盘数字、各种特殊符号等。
  6. 这里我使用的是QKeyEvent指针进行发送,由于QKeyEvent没有默认无参构造,所以在Linux下不支持使用信号发送QKeyEvent变量,所以只能使用指针;
  7. 因为传递的是指针,所以在接收信号的槽函数里使用完后需要Delete,避免内存泄漏;
  8. 简易这个信号只绑定一次,避免多个槽函数里使用同一个指针,一个槽函数释放了另外一个槽函数里出现野指针或者重复释放。
  9. 不使用时需要使用UnhookWindowsHookEx()函数删除 SetWindowsHookEx ()函数在挂钩链中安装的挂钩过程。

4、关键代码🍋

  • 由于使用到了系统API,所以pro文件中需要链接系统库
win32 {
LIBS+= -luser32    # 使用WindowsAPI需要链接库
}
  • globalkeyevent.h
#ifndef GLOBALKEYEVENT_H
#define GLOBALKEYEVENT_H

#include <QObject>
class QKeyEvent;

/**
 *  全局鼠标事件单例信号类
 */
class GlobalKeyEvent : public QObject
{
    Q_OBJECT
public:
    static GlobalKeyEvent* getInstance()
    {
        static GlobalKeyEvent keyEvent;
        return &keyEvent;
    }

    static bool installKeyEvent();      // 安装全局键盘事件监听器
    static bool removeKeyEvent();       // 卸载全局键盘事件监听器

signals:
    /**
     * @brief 由于传递的是指针,为了保证不会出现内存泄露,需要在槽函数中delete。
     *        建议此信号只绑定一次,因为如果绑定多次可能会出现一个槽函数里把信号delete了,另外一个槽函数还在使用,出现野指针,或者多个槽函数多次delete
     */
    void keyEvent(QKeyEvent* event);

private:
    GlobalKeyEvent(){}
};

#endif // GLOBALKEYEVENT_H

  • globalkeyevent_win.cpp
#include "globalkeyevent.h"
#if defined(Q_OS_WIN)
#include "Windows.h"
#include <QDebug>
#include <QKeyEvent>

enum WindowsNativeModifiers {
    ShiftLeft            = 0x00000001,
    ControlLeft          = 0x00000002,
    AltLeft              = 0x00000004,
    MetaLeft             = 0x00000008,
    ShiftRight           = 0x00000010,
    ControlRight         = 0x00000020,
    AltRight             = 0x00000040,
    MetaRight            = 0x00000080,
    CapsLock             = 0x00000100,
    NumLock              = 0x00000200,
    ScrollLock           = 0x00000400,
    ExtendedKey          = 0x01000000,

    // Convenience mappings
    ShiftAny             = 0x00000011,
    ControlAny           = 0x00000022,
    AltAny               = 0x00000044,
    MetaAny              = 0x00000088,
    LockAny              = 0x00000700
};

// 值的含义:
// 0           = 字符输出键,需要键盘驱动程序映射
// Key_unknown = 未知虚拟密钥,无法转换,忽略
// Windows 到 Qt::Key映射表,在"D:\Qt\Qt5.12.5\5.12.5\Src\qtbase\src\plugins\platforms\windows\qwindowskeymapper.cpp"中有
static const uint KeyTbl[] = { // Keyboard mapping table
                        // Dec |  Hex | Windows Virtual key
    Qt::Key_unknown,    //   0   0x00
    Qt::Key_unknown,    //   1   0x01   VK_LBUTTON          | Left mouse button
    Qt::Key_unknown,    //   2   0x02   VK_RBUTTON          | Right mouse button
    Qt::Key_Cancel,     //   3   0x03   VK_CANCEL           | Control-Break processing
    Qt::Key_unknown,    //   4   0x04   VK_MBUTTON          | Middle mouse button
    Qt::Key_unknown,    //   5   0x05   VK_XBUTTON1         | X1 mouse button
    Qt::Key_unknown,    //   6   0x06   VK_XBUTTON2         | X2 mouse button
    Qt::Key_unknown,    //   7   0x07   -- unassigned --
    Qt::Key_Backspace,  //   8   0x08   VK_BACK             | BackSpace key
    Qt::Key_Tab,        //   9   0x09   VK_TAB              | Tab key
    Qt::Key_unknown,    //  10   0x0A   -- reserved --
    Qt::Key_unknown,    //  11   0x0B   -- reserved --
    Qt::Key_Clear,      //  12   0x0C   VK_CLEAR            | Clear key
    Qt::Key_Return,     //  13   0x0D   VK_RETURN           | Enter key
    Qt::Key_unknown,    //  14   0x0E   -- unassigned --
    Qt::Key_unknown,    //  15   0x0F   -- unassigned --
    Qt::Key_Shift,      //  16   0x10   VK_SHIFT            | Shift key
    Qt::Key_Control,    //  17   0x11   VK_CONTROL          | Ctrl key
    Qt::Key_Alt,        //  18   0x12   VK_MENU             | Alt key
    Qt::Key_Pause,      //  19   0x13   VK_PAUSE            | Pause key
    Qt::Key_CapsLock,   //  20   0x14   VK_CAPITAL          | Caps-Lock
    Qt::Key_unknown,    //  21   0x15   VK_KANA / VK_HANGUL | IME Kana or Hangul mode
    Qt::Key_unknown,    //  22   0x16   -- unassigned --
    Qt::Key_unknown,    //  23   0x17   VK_JUNJA            | IME Junja mode
    Qt::Key_unknown,    //  24   0x18   VK_FINAL            | IME final mode
    Qt::Key_unknown,    //  25   0x19   VK_HANJA / VK_KANJI | IME Hanja or Kanji mode
    Qt::Key_unknown,    //  26   0x1A   -- unassigned --
    Qt::Key_Escape,     //  27   0x1B   VK_ESCAPE           | Esc key
    Qt::Key_unknown,    //  28   0x1C   VK_CONVERT          | IME convert
    Qt::Key_unknown,    //  29   0x1D   VK_NONCONVERT       | IME non-convert
    Qt::Key_unknown,    //  30   0x1E   VK_ACCEPT           | IME accept
    Qt::Key_Mode_switch,//  31   0x1F   VK_MODECHANGE       | IME mode change request
    Qt::Key_Space,      //  32   0x20   VK_SPACE            | Spacebar
    Qt::Key_PageUp,     //  33   0x21   VK_PRIOR            | Page Up key
    Qt::Key_PageDown,   //  34   0x22   VK_NEXT             | Page Down key
    Qt::Key_End,        //  35   0x23   VK_END              | End key
    Qt::Key_Home,       //  36   0x24   VK_HOME             | Home key
    Qt::Key_Left,       //  37   0x25   VK_LEFT             | Left arrow key
    Qt::Key_Up,         //  38   0x26   VK_UP               | Up arrow key
    Qt::Key_Right,      //  39   0x27   VK_RIGHT            | Right arrow key
    Qt::Key_Down,       //  40   0x28   VK_DOWN             | Down arrow key
    Qt::Key_Select,     //  41   0x29   VK_SELECT           | Select key
    Qt::Key_Printer,    //  42   0x2A   VK_PRINT            | Print key
    Qt::Key_Execute,    //  43   0x2B   VK_EXECUTE          | Execute key
    Qt::Key_Print,      //  44   0x2C   VK_SNAPSHOT         | Print Screen key
    Qt::Key_Insert,     //  45   0x2D   VK_INSERT           | Ins key
    Qt::Key_Delete,     //  46   0x2E   VK_DELETE           | Del key
    Qt::Key_Help,       //  47   0x2F   VK_HELP             | Help key
    0,                  //  48   0x30   (VK_0)              | 0 key
    0,                  //  49   0x31   (VK_1)              | 1 key
    0,                  //  50   0x32   (VK_2)              | 2 key
    0,                  //  51   0x33   (VK_3)              | 3 key
    0,                  //  52   0x34   (VK_4)              | 4 key
    0,                  //  53   0x35   (VK_5)              | 5 key
    0,                  //  54   0x36   (VK_6)              | 6 key
    0,                  //  55   0x37   (VK_7)              | 7 key
    0,                  //  56   0x38   (VK_8)              | 8 key
    0,                  //  57   0x39   (VK_9)              | 9 key
    Qt::Key_unknown,    //  58   0x3A   -- unassigned --
    Qt::Key_unknown,    //  59   0x3B   -- unassigned --
    Qt::Key_unknown,    //  60   0x3C   -- unassigned --
    Qt::Key_unknown,    //  61   0x3D   -- unassigned --
    Qt::Key_unknown,    //  62   0x3E   -- unassigned --
    Qt::Key_unknown,    //  63   0x3F   -- unassigned --
    Qt::Key_unknown,    //  64   0x40   -- unassigned --
    0,                  //  65   0x41   (VK_A)              | A key
    0,                  //  66   0x42   (VK_B)              | B key
    0,                  //  67   0x43   (VK_C)              | C key
    0,                  //  68   0x44   (VK_D)              | D key
    0,                  //  69   0x45   (VK_E)              | E key
    0,                  //  70   0x46   (VK_F)              | F key
    0,                  //  71   0x47   (VK_G)              | G key
    0,                  //  72   0x48   (VK_H)              | H key
    0,                  //  73   0x49   (VK_I)              | I key
    0,                  //  74   0x4A   (VK_J)              | J key
    0,                  //  75   0x4B   (VK_K)              | K key
    0,                  //  76   0x4C   (VK_L)              | L key
    0,                  //  77   0x4D   (VK_M)              | M key
    0,                  //  78   0x4E   (VK_N)              | N key
    0,                  //  79   0x4F   (VK_O)              | O key
    0,                  //  80   0x50   (VK_P)              | P key
    0,                  //  81   0x51   (VK_Q)              | Q key
    0,                  //  82   0x52   (VK_R)              | R key
    0,                  //  83   0x53   (VK_S)              | S key
    0,                  //  84   0x54   (VK_T)              | T key
    0,                  //  85   0x55   (VK_U)              | U key
    0,                  //  86   0x56   (VK_V)              | V key
    0,                  //  87   0x57   (VK_W)              | W key
    0,                  //  88   0x58   (VK_X)              | X key
    0,                  //  89   0x59   (VK_Y)              | Y key
    0,                  //  90   0x5A   (VK_Z)              | Z key
    Qt::Key_Meta,       //  91   0x5B   VK_LWIN             | Left Windows  - MS Natural kbd
    Qt::Key_Meta,       //  92   0x5C   VK_RWIN             | Right Windows - MS Natural kbd
    Qt::Key_Menu,       //  93   0x5D   VK_APPS             | Application key-MS Natural kbd
    Qt::Key_unknown,    //  94   0x5E   -- reserved --
    Qt::Key_Sleep,      //  95   0x5F   VK_SLEEP
    Qt::Key_0,          //  96   0x60   VK_NUMPAD0          | Numeric keypad 0 key
    Qt::Key_1,          //  97   0x61   VK_NUMPAD1          | Numeric keypad 1 key
    Qt::Key_2,          //  98   0x62   VK_NUMPAD2          | Numeric keypad 2 key
    Qt::Key_3,          //  99   0x63   VK_NUMPAD3          | Numeric keypad 3 key
    Qt::Key_4,          // 100   0x64   VK_NUMPAD4          | Numeric keypad 4 key
    Qt::Key_5,          // 101   0x65   VK_NUMPAD5          | Numeric keypad 5 key
    Qt::Key_6,          // 102   0x66   VK_NUMPAD6          | Numeric keypad 6 key
    Qt::Key_7,          // 103   0x67   VK_NUMPAD7          | Numeric keypad 7 key
    Qt::Key_8,          // 104   0x68   VK_NUMPAD8          | Numeric keypad 8 key
    Qt::Key_9,          // 105   0x69   VK_NUMPAD9          | Numeric keypad 9 key
    Qt::Key_Asterisk,   // 106   0x6A   VK_MULTIPLY         | Multiply key
    Qt::Key_Plus,       // 107   0x6B   VK_ADD              | Add key
    Qt::Key_unknown,    // 108   0x6C   VK_SEPARATOR        | Separator key (locale-dependent)
    Qt::Key_Minus,      // 109   0x6D   VK_SUBTRACT         | Subtract key
    Qt::Key_unknown,    // 110   0x6E   VK_DECIMAL          | Decimal key (locale-dependent)
    Qt::Key_Slash,      // 111   0x6F   VK_DIVIDE           | Divide key
    Qt::Key_F1,         // 112   0x70   VK_F1               | F1 key
    Qt::Key_F2,         // 113   0x71   VK_F2               | F2 key
    Qt::Key_F3,         // 114   0x72   VK_F3               | F3 key
    Qt::Key_F4,         // 115   0x73   VK_F4               | F4 key
    Qt::Key_F5,         // 116   0x74   VK_F5               | F5 key
    Qt::Key_F6,         // 117   0x75   VK_F6               | F6 key
    Qt::Key_F7,         // 118   0x76   VK_F7               | F7 key
    Qt::Key_F8,         // 119   0x77   VK_F8               | F8 key
    Qt::Key_F9,         // 120   0x78   VK_F9               | F9 key
    Qt::Key_F10,        // 121   0x79   VK_F10              | F10 key
    Qt::Key_F11,        // 122   0x7A   VK_F11              | F11 key
    Qt::Key_F12,        // 123   0x7B   VK_F12              | F12 key
    Qt::Key_F13,        // 124   0x7C   VK_F13              | F13 key
    Qt::Key_F14,        // 125   0x7D   VK_F14              | F14 key
    Qt::Key_F15,        // 126   0x7E   VK_F15              | F15 key
    Qt::Key_F16,        // 127   0x7F   VK_F16              | F16 key
    Qt::Key_F17,        // 128   0x80   VK_F17              | F17 key
    Qt::Key_F18,        // 129   0x81   VK_F18              | F18 key
    Qt::Key_F19,        // 130   0x82   VK_F19              | F19 key
    Qt::Key_F20,        // 131   0x83   VK_F20              | F20 key
    Qt::Key_F21,        // 132   0x84   VK_F21              | F21 key
    Qt::Key_F22,        // 133   0x85   VK_F22              | F22 key
    Qt::Key_F23,        // 134   0x86   VK_F23              | F23 key
    Qt::Key_F24,        // 135   0x87   VK_F24              | F24 key
    Qt::Key_unknown,    // 136   0x88   -- unassigned --
    Qt::Key_unknown,    // 137   0x89   -- unassigned --
    Qt::Key_unknown,    // 138   0x8A   -- unassigned --
    Qt::Key_unknown,    // 139   0x8B   -- unassigned --
    Qt::Key_unknown,    // 140   0x8C   -- unassigned --
    Qt::Key_unknown,    // 141   0x8D   -- unassigned --
    Qt::Key_unknown,    // 142   0x8E   -- unassigned --
    Qt::Key_unknown,    // 143   0x8F   -- unassigned --
    Qt::Key_NumLock,    // 144   0x90   VK_NUMLOCK          | Num Lock key
    Qt::Key_ScrollLock, // 145   0x91   VK_SCROLL           | Scroll Lock key
                        // Fujitsu/OASYS kbd --------------------
    0, //Qt::Key_Jisho, // 146   0x92   VK_OEM_FJ_JISHO     | 'Dictionary' key /
                        //              VK_OEM_NEC_EQUAL  = key on numpad on NEC PC-9800 kbd
    Qt::Key_Massyo,     // 147   0x93   VK_OEM_FJ_MASSHOU   | 'Unregister word' key
    Qt::Key_Touroku,    // 148   0x94   VK_OEM_FJ_TOUROKU   | 'Register word' key
    0, //Qt::Key_Oyayubi_Left,//149   0x95  VK_OEM_FJ_LOYA  | 'Left OYAYUBI' key
    0, //Qt::Key_Oyayubi_Right,//150  0x96  VK_OEM_FJ_ROYA  | 'Right OYAYUBI' key
    Qt::Key_unknown,    // 151   0x97   -- unassigned --
    Qt::Key_unknown,    // 152   0x98   -- unassigned --
    Qt::Key_unknown,    // 153   0x99   -- unassigned --
    Qt::Key_unknown,    // 154   0x9A   -- unassigned --
    Qt::Key_unknown,    // 155   0x9B   -- unassigned --
    Qt::Key_unknown,    // 156   0x9C   -- unassigned --
    Qt::Key_unknown,    // 157   0x9D   -- unassigned --
    Qt::Key_unknown,    // 158   0x9E   -- unassigned --
    Qt::Key_unknown,    // 159   0x9F   -- unassigned --
    Qt::Key_Shift,      // 160   0xA0   VK_LSHIFT           | Left Shift key
    Qt::Key_Shift,      // 161   0xA1   VK_RSHIFT           | Right Shift key
    Qt::Key_Control,    // 162   0xA2   VK_LCONTROL         | Left Ctrl key
    Qt::Key_Control,    // 163   0xA3   VK_RCONTROL         | Right Ctrl key
    Qt::Key_Alt,        // 164   0xA4   VK_LMENU            | Left Menu key
    Qt::Key_Alt,        // 165   0xA5   VK_RMENU            | Right Menu key
    Qt::Key_Back,       // 166   0xA6   VK_BROWSER_BACK     | Browser Back key
    Qt::Key_Forward,    // 167   0xA7   VK_BROWSER_FORWARD  | Browser Forward key
    Qt::Key_Refresh,    // 168   0xA8   VK_BROWSER_REFRESH  | Browser Refresh key
    Qt::Key_Stop,       // 169   0xA9   VK_BROWSER_STOP     | Browser Stop key
    Qt::Key_Search,     // 170   0xAA   VK_BROWSER_SEARCH   | Browser Search key
    Qt::Key_Favorites,  // 171   0xAB   VK_BROWSER_FAVORITES| Browser Favorites key
    Qt::Key_HomePage,   // 172   0xAC   VK_BROWSER_HOME     | Browser Start and Home key
    Qt::Key_VolumeMute, // 173   0xAD   VK_VOLUME_MUTE      | Volume Mute key
    Qt::Key_VolumeDown, // 174   0xAE   VK_VOLUME_DOWN      | Volume Down key
    Qt::Key_VolumeUp,   // 175   0xAF   VK_VOLUME_UP        | Volume Up key
    Qt::Key_MediaNext,  // 176   0xB0   VK_MEDIA_NEXT_TRACK | Next Track key
    Qt::Key_MediaPrevious, //177 0xB1   VK_MEDIA_PREV_TRACK | Previous Track key
    Qt::Key_MediaStop,  // 178   0xB2   VK_MEDIA_STOP       | Stop Media key
    Qt::Key_MediaPlay,  // 179   0xB3   VK_MEDIA_PLAY_PAUSE | Play/Pause Media key
    Qt::Key_LaunchMail, // 180   0xB4   VK_LAUNCH_MAIL      | Start Mail key
    Qt::Key_LaunchMedia,// 181   0xB5   VK_LAUNCH_MEDIA_SELECT Select Media key
    Qt::Key_Launch0,    // 182   0xB6   VK_LAUNCH_APP1      | Start Application 1 key
    Qt::Key_Launch1,    // 183   0xB7   VK_LAUNCH_APP2      | Start Application 2 key
    Qt::Key_unknown,    // 184   0xB8   -- reserved --
    Qt::Key_unknown,    // 185   0xB9   -- reserved --
    0,                  // 186   0xBA   VK_OEM_1            | ';:' for US
    0,                  // 187   0xBB   VK_OEM_PLUS         | '+' any country
    0,                  // 188   0xBC   VK_OEM_COMMA        | ',' any country
    0,                  // 189   0xBD   VK_OEM_MINUS        | '-' any country
    0,                  // 190   0xBE   VK_OEM_PERIOD       | '.' any country
    0,                  // 191   0xBF   VK_OEM_2            | '/?' for US
    0,                  // 192   0xC0   VK_OEM_3            | '`~' for US
    Qt::Key_unknown,    // 193   0xC1   -- reserved --
    Qt::Key_unknown,    // 194   0xC2   -- reserved --
    Qt::Key_unknown,    // 195   0xC3   -- reserved --
    Qt::Key_unknown,    // 196   0xC4   -- reserved --
    Qt::Key_unknown,    // 197   0xC5   -- reserved --
    Qt::Key_unknown,    // 198   0xC6   -- reserved --
    Qt::Key_unknown,    // 199   0xC7   -- reserved --
    Qt::Key_unknown,    // 200   0xC8   -- reserved --
    Qt::Key_unknown,    // 201   0xC9   -- reserved --
    Qt::Key_unknown,    // 202   0xCA   -- reserved --
    Qt::Key_unknown,    // 203   0xCB   -- reserved --
    Qt::Key_unknown,    // 204   0xCC   -- reserved --
    Qt::Key_unknown,    // 205   0xCD   -- reserved --
    Qt::Key_unknown,    // 206   0xCE   -- reserved --
    Qt::Key_unknown,    // 207   0xCF   -- reserved --
    Qt::Key_unknown,    // 208   0xD0   -- reserved --
    Qt::Key_unknown,    // 209   0xD1   -- reserved --
    Qt::Key_unknown,    // 210   0xD2   -- reserved --
    Qt::Key_unknown,    // 211   0xD3   -- reserved --
    Qt::Key_unknown,    // 212   0xD4   -- reserved --
    Qt::Key_unknown,    // 213   0xD5   -- reserved --
    Qt::Key_unknown,    // 214   0xD6   -- reserved --
    Qt::Key_unknown,    // 215   0xD7   -- reserved --
    Qt::Key_unknown,    // 216   0xD8   -- unassigned --
    Qt::Key_unknown,    // 217   0xD9   -- unassigned --
    Qt::Key_unknown,    // 218   0xDA   -- unassigned --
    0,                  // 219   0xDB   VK_OEM_4            | '[{' for US
    0,                  // 220   0xDC   VK_OEM_5            | '\|' for US
    0,                  // 221   0xDD   VK_OEM_6            | ']}' for US
    0,                  // 222   0xDE   VK_OEM_7            | ''"' for US
    0,                  // 223   0xDF   VK_OEM_8
    Qt::Key_unknown,    // 224   0xE0   -- reserved --
    Qt::Key_unknown,    // 225   0xE1   VK_OEM_AX           | 'AX' key on Japanese AX kbd
    Qt::Key_unknown,    // 226   0xE2   VK_OEM_102          | "<>" or "\|" on RT 102-key kbd
    Qt::Key_unknown,    // 227   0xE3   VK_ICO_HELP         | Help key on ICO
    Qt::Key_unknown,    // 228   0xE4   VK_ICO_00           | 00 key on ICO
    Qt::Key_unknown,    // 229   0xE5   VK_PROCESSKEY       | IME Process key
    Qt::Key_unknown,    // 230   0xE6   VK_ICO_CLEAR        |
    Qt::Key_unknown,    // 231   0xE7   VK_PACKET           | Unicode char as keystrokes
    Qt::Key_unknown,    // 232   0xE8   -- unassigned --
                        // Nokia/Ericsson definitions ---------------
    Qt::Key_unknown,    // 233   0xE9   VK_OEM_RESET
    Qt::Key_unknown,    // 234   0xEA   VK_OEM_JUMP
    Qt::Key_unknown,    // 235   0xEB   VK_OEM_PA1
    Qt::Key_unknown,    // 236   0xEC   VK_OEM_PA2
    Qt::Key_unknown,    // 237   0xED   VK_OEM_PA3
    Qt::Key_unknown,    // 238   0xEE   VK_OEM_WSCTRL
    Qt::Key_unknown,    // 239   0xEF   VK_OEM_CUSEL
    Qt::Key_unknown,    // 240   0xF0   VK_OEM_ATTN
    Qt::Key_unknown,    // 241   0xF1   VK_OEM_FINISH
    Qt::Key_unknown,    // 242   0xF2   VK_OEM_COPY
    Qt::Key_unknown,    // 243   0xF3   VK_OEM_AUTO
    Qt::Key_unknown,    // 244   0xF4   VK_OEM_ENLW
    Qt::Key_unknown,    // 245   0xF5   VK_OEM_BACKTAB
    Qt::Key_unknown,    // 246   0xF6   VK_ATTN             | Attn key
    Qt::Key_unknown,    // 247   0xF7   VK_CRSEL            | CrSel key
    Qt::Key_unknown,    // 248   0xF8   VK_EXSEL            | ExSel key
    Qt::Key_unknown,    // 249   0xF9   VK_EREOF            | Erase EOF key
    Qt::Key_Play,       // 250   0xFA   VK_PLAY             | Play key
    Qt::Key_Zoom,       // 251   0xFB   VK_ZOOM             | Zoom key
    Qt::Key_unknown,    // 252   0xFC   VK_NONAME           | Reserved
    Qt::Key_unknown,    // 253   0xFD   VK_PA1              | PA1 key
    Qt::Key_Clear,      // 254   0xFE   VK_OEM_CLEAR        | Clear key
    0
};

static inline quint32 winceKeyBend(quint32 keyCode)
{
    return KeyTbl[keyCode];
}

/**
 * @brief  获取是否按下键盘修饰键,例如Ctrl、shirt等
 * @return
 */
static inline Qt::KeyboardModifiers queryKeyboardModifiers()
{
    Qt::KeyboardModifiers modifiers = Qt::NoModifier;
    if (GetKeyState(VK_SHIFT) < 0)
        modifiers |= Qt::ShiftModifier;
    if (GetKeyState(VK_CONTROL) < 0)
        modifiers |= Qt::ControlModifier;
    if (GetKeyState(VK_MENU) < 0)
        modifiers |= Qt::AltModifier;
    if (GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0)
        modifiers |= Qt::MetaModifier;
    return modifiers;
}


static uchar g_buffer[256];
/**
 * @brief            将VK转换为Qt键代码或unicode字符
 * @param vk
 * @param scancode
 * @param g_buffer
 * @param isDeadkey
 * @return
 */
static inline quint32 toKeyOrUnicode(quint32 vk, quint32 scancode, bool *isDeadkey = nullptr)
{
    Q_ASSERT(vk > 0 && vk < 256);

    GetKeyboardState(g_buffer);   // // 将 256 个虚拟密钥的状态复制到指定的缓冲区。
//    qDebug() <<"vk " << (g_buffer[vk] & 0x80);
    g_buffer[VK_LWIN    ] = 0;
    g_buffer[VK_RWIN    ] = 0;
    g_buffer[VK_CAPITAL ] = 0;
    g_buffer[VK_NUMLOCK ] = 0;
    g_buffer[VK_SCROLL  ] = 0;
    // 始终为0,因为我们只会更改其他版本
    g_buffer[VK_RSHIFT  ] = 0;
    g_buffer[VK_RCONTROL] = 0;
    g_buffer[VK_LMENU   ] = 0;   // 使用右Alt,因为左Ctrl+右Alt被视为AltGraph
    g_buffer[VK_CONTROL ] = 0;   // 需要将ctrl键清空,否则按住Ctrl + 字母键会出现无法返回正确值的情况

    quint32 code = 0;
    QChar unicodeBuffer[5];
    int res = ToUnicode(vk, scancode, g_buffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
    // 使用Ctrl修饰符时,ToUnicode不会返回正确的值。为了分配右键,如果上一次调用失败,则仅删除该函数的控制修饰符。
    if (res == 0 && g_buffer[VK_CONTROL])
    {
        const unsigned char controlState = g_buffer[VK_CONTROL];
        g_buffer[VK_CONTROL] = 0;
        res = ToUnicode(vk, scancode, g_buffer, reinterpret_cast<LPWSTR>(unicodeBuffer), 5, 0);
        g_buffer[VK_CONTROL] = controlState;
    }
    if (res)
    {
        code = unicodeBuffer[0].toUpper().unicode();   // 将字符转换位Qt::Key值
    }

    // Qt::Key_*的编码不低于0x20,因此请重试,DEL键(0x7f)使用正确的Qt::Key_ code
    if (code < 0x20 || code == 0x7f) // Handles res==0 too
    {
        code = winceKeyBend(vk);
    }

    if (isDeadkey)
    {
        *isDeadkey = (res == -1);
    }

    return code == Qt::Key_unknown ? 0 : code;
}


static uchar g_keyState[256];
/**
 * @brief           获取当前按下键的字符
 * @param vk        要转换的虚拟密钥代码
 * @param scancode  要转换的密钥的硬件扫描代码
 * @return
 */
static inline QString getKeyText(quint32 vk, quint32 scancode)
{
    GetKeyboardState(g_keyState);                                     // 将 256 个虚拟密钥的状态复制到指定的缓冲区。
    wchar_t newKey[3] = {0};
    // 将指定的虚拟键代码和键盘状态转换为相应的一个或多个 Unicode 字符。
    int ret = ToUnicode(vk,           // 要转换的虚拟密钥代码
                        scancode,     // 要转换的密钥的硬件扫描代码
                        g_keyState,   // 指向包含当前键盘状态的 256 字节数组的指针。数组中的每个元素(字节)都包含一个键的状态。
                        newKey,       // 接收转换后的一个或多个 Unicode 字符的缓冲区
                        3,            // 参数指向的缓冲区的大小(以字符为单位)
                        0);           // 函数的行为。0:则菜单处于活动状态; 2,则不会更改键盘状态(Windows 10 版本 1607 及更高版本)

    if (ret == 1)      // 1:一个字符被写入 newKey
    {
        QChar uch = QChar(newKey[0]);
        return  uch;
    }
    else        // 无法获取到字符
    {
        return QString();
    }
}

/**
 * @brief        获取本机修改器值
 * @param flags
 * @return
 */
static quint32 getNativeModifiers(quint32 flags)
{
    quint32 nModifiers = 0;


    // 将本机修改器映射到某些位表示
    nModifiers |= (GetKeyState(VK_LSHIFT  ) & 0x80 ? ShiftLeft : 0);
    nModifiers |= (GetKeyState(VK_RSHIFT  ) & 0x80 ? ShiftRight : 0);
    nModifiers |= (GetKeyState(VK_LCONTROL) & 0x80 ? ControlLeft : 0);
    nModifiers |= (GetKeyState(VK_RCONTROL) & 0x80 ? ControlRight : 0);
    nModifiers |= (GetKeyState(VK_LMENU   ) & 0x80 ? AltLeft : 0);
    nModifiers |= (GetKeyState(VK_RMENU   ) & 0x80 ? AltRight : 0);
    nModifiers |= (GetKeyState(VK_LWIN    ) & 0x80 ? MetaLeft : 0);
    nModifiers |= (GetKeyState(VK_RWIN    ) & 0x80 ? MetaRight : 0);
    // 将锁定键添加到相同的位
    nModifiers |= (GetKeyState(VK_CAPITAL ) & 0x01 ? CapsLock : 0);
    nModifiers |= (GetKeyState(VK_NUMLOCK ) & 0x01 ? NumLock : 0);
    nModifiers |= (GetKeyState(VK_SCROLL  ) & 0x01 ? ScrollLock : 0);

    if (flags & LLKHF_EXTENDED)   // 使用扩展键,例如:Home End pgUP pgDn
    {
        nModifiers |= ExtendedKey;
    }

    return nModifiers;
}

static HHOOK g_hook = nullptr;
/**
 * @brief           处理键盘事件的回调函数,由于这不是一个成员函数,所以需要通过中间单例类GlobalKeyEvent将键盘信号传递出来
 *                  具体内容看https://learn.microsoft.com/zh-cn/previous-versions/windows/desktop/legacy/ms644985(v=vs.85)
 * @param nCode     挂钩过程用于确定如何处理消息的代码。如果nCode小于零,则挂钩过程必须将消息传递给 CallNextHookEx 函数而不进行进一步处理,并且应返回CallNextHookEx返回的值
 * @param wParam    信号类型:WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN或WM_SYSKEYUP
 * @param lParam    KBDLLHOOKSTRUCT结构体指针
 * @return
 */
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    KBDLLHOOKSTRUCT * kbdll = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);

    bool isDeadKey                  = false;
    Qt::KeyboardModifiers modifiers = queryKeyboardModifiers();
    int                   key       = int(toKeyOrUnicode(kbdll->vkCode, kbdll->scanCode, &isDeadKey));
    QString               text      = getKeyText(kbdll->vkCode, kbdll->scanCode);
    quint32         nativeModifiers = getNativeModifiers(kbdll->flags);
    bool                  autorep   = (g_buffer[kbdll->vkCode] & 0x80);

    switch (wParam)
    {
    case WM_KEYDOWN:      // 按下非系统键, 非系统键是未按下 ALT 键时按下的键
    {
        emit GlobalKeyEvent::getInstance()->keyEvent(new QKeyEvent(QEvent::KeyPress, key, modifiers, kbdll->scanCode, kbdll->vkCode, nativeModifiers, text, autorep));
        break;
    }
    case WM_KEYUP:        // 当释放非系统键
        emit GlobalKeyEvent::getInstance()->keyEvent(new QKeyEvent(QEvent::KeyRelease, key, modifiers, kbdll->scanCode, kbdll->vkCode, nativeModifiers, text, !autorep));
        break;
    case WM_SYSKEYDOWN:   // 当用户按下 F10 键 (激活菜单栏) 或按住 Alt 键,然后按另一个键时,发布到具有键盘焦点的窗口
        qDebug() << "按下系统键 Alt";
        emit GlobalKeyEvent::getInstance()->keyEvent(new QKeyEvent(QEvent::KeyPress, key, modifiers, kbdll->scanCode, kbdll->vkCode, nativeModifiers, text, autorep));
        break;
    case WM_SYSKEYUP:     // 当用户释放按下 Alt 键时按下的键
        qDebug() << "释放系统键 Alt";
        emit GlobalKeyEvent::getInstance()->keyEvent(new QKeyEvent(QEvent::KeyRelease, key, modifiers, kbdll->scanCode, kbdll->vkCode, nativeModifiers, text, !autorep));
        break;
    default:
        break;
    }

    return CallNextHookEx(nullptr, nCode, wParam, lParam);   // 注意这一行一定不能少,否则会出大问题
}


/**
 * @brief  安装全局鼠标事件监听器
 * @return
 */
bool GlobalKeyEvent::installKeyEvent()
{
    if(g_hook) return true;     // 避免重复安装
    /**
     * WH_KEYBOARD_LL 为全局键盘钩子, WH_MOUSE_LL 为全局鼠标钩子
     * 详细说明看官方文档:https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setwindowshookexw
     */
    g_hook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandleW(nullptr), 0);
    return g_hook;
}

/**
 * @brief   卸载全局鼠标事件监听器
 * @return
 */
bool GlobalKeyEvent::removeKeyEvent()
{
    if(!g_hook) return true;   // 避免重复卸载
    bool ret = UnhookWindowsHookEx(g_hook);
    if(ret)
    {
        g_hook = nullptr;
        return true;
    }
    return false;
}


#endif

5、源代码🍌

  • gitee
  • github
  • 全局鼠标键盘事件监听器仓库github
  • 全局鼠标键盘事件监听器仓库gitee
  • CSDN
  • 可以使用命令git clone https://gitee.com/mahuifa/QtGlobalEvent.git直接下载仓库,然后引用到自己的程序中。

🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒🍒

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/82684.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分布式文件存储-FastDFS环境搭建

FastDFS简介 FastDFS是什么&#xff1f;我们这里可以看一下度娘的解释。FastDFS是一个开源的轻量级分布式文件系统&#xff0c;它对文件进行管理&#xff0c;功能包括&#xff1a;文件存储、文件同步、文件访问&#xff08;文件上传、文件下载&#xff09;等&#xff0c;解决了…

grid布局 笔记

这次给大家带来的是grid布局&#xff0c;之前听说过但没接触过&#xff0c;一起来学一下吧&#xff0c;提升一下css认知。 1、开启方式 给父级盒子开启grid布局 display: grid2、gap 间隙 所有盒子的行列间隙为10px gap: 10px行/列 colomn-gap: 20px;row-gap: 15px注意&#…

java计算机毕业设计ssm迎新系统pv83c(附源码、数据库)

java计算机毕业设计ssm迎新系统pv83c&#xff08;附源码、数据库&#xff09; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项…

[MySQL]-压力测试之性能监测指标

[MySQL]-压力测试之性能监测指标 森格 | 2022年12月 本文主要是介绍在数据库的压测过程中&#xff0c;要时刻的一些指标&#xff0c;尤其是当数据库的性能达到瓶颈时&#xff0c;要注意哪个指标已经打满。 一、前文回顾 在前面量两篇文章中&#xff0c;分别介绍了两款压测数据…

数据标注过程中会遇到哪些难题 | 景联文科技

“近年来&#xff0c;⼈⼯智能发展迅速&#xff0c;影响着我们⽣活中的方方面面。随着⼈⼯智能应⽤场景不断增加&#xff0c;作为⼈⼯智能的上游基础⾏业&#xff0c;数据标注行业也快速发展&#xff0c;但许多不成熟的标注团队组织由于管理水平有限&#xff0c;且没有系统的标…

【C++笔试强训】第五天

文章目录选择题编程题选择题 在上下文和头文件均正常情况下&#xff0c;以下程序的输出结果是&#xff08;&#xff09; int x 1; do{printf("%2d\n",x) }while(x--);A 1 B 无任何输出 C 2 D 陷入死循环 x1&#xff0c;x是后置&#xff0c;先使用&#xff0c;在加…

Python之路200个小例子网页版,真诚奉献,从一而终!

前言 历史两个月&#xff0c;利用所有业余时间&#xff0c;与朋友一起搜集、创作Python小例子&#xff0c;截止目前已超过200个例子&#xff1a; Python之路 1 简洁之美 2 Python绘图 3 Python动画 4 Python数据分析 5 Python机器学习 6 Python-GUI 一、Python基础 1…

Acrel-EIOT免调试抄表系统

安科瑞 华楠 一、远程抄表系统应用场景-互联网用户&#xff08;能源参与者&#xff09; 二、远程抄表系统互联网用户的痛点 2.1 数据系统价格偏高 一套系统需要前期勘察、画图、出方案、报价&#xff0c;建设投入服务器、网络布线、交换机&#xff0c;网关、人工施工及调试&…

Vue中使用vxe-table组件分页查询,多页选择数据回显,分页记录保存选中的数据

官方示例&#xff1a;vxe-table v3https://vxetable.cn/v3/#/table/advanced/page 当表格中需要渲染的数据量比较大&#xff0c;有几万几十万条数据时&#xff0c;在前端分页将会非常慢&#xff0c;建议将当前页码和每页数量传递个后端&#xff0c;后端分好后给前端渲染。 后端…

电脑重装小白系统没内存怎该如何解决

很多网友想要重装系统&#xff0c;但是在用小白系统安装的时候提示没内存怎么办呢?我们可以卸载自己的电脑软件&#xff0c;或者是扩充一下自己电脑内存。不知道如何处理相关问题的小伙伴可以直接看看下面小编分享的文章小白系统没内存怎么办。 工具/原料&#xff1a; 系统版…

MySQL主从复制-----读写分离

MySQL主从复制是一个异步的复制过程&#xff0c;底层是基于Mysql数据库自带的二进制日志功能。就是一台或多台Mysql数据库&#xff08;slave从库&#xff09;从另一台mysql数据库&#xff08;master,主库&#xff09;进行日志的复制然后再解析日志并应用到自身&#xff0c;最终…

nacos--基础--2.5--部署--集群--传统

nacos–基础–2.5–部署–集群–传统 前提 使用hd用户登陆 完成基础环境搭建https://blog.csdn.net/zhou920786312/article/details/1182123021、机器 IP端口备注192.168.187.1718848xx192.168.187.1728848xx192.168.187.1738848xx 2、tar.gz安装 2.1、安装 参考 nacos–基…

javaee之黑马旅游网4

下面来做旅游线路的查询功能 1.查询参数传递 在header.html 我们先找到这部分的位置 上面就加了两个id 引入这个js文件 控制搜索框 那我们现在来说一下&#xff0c;cid怎么来获取&#xff0c;换句话说cid是从什么地方传过来的&#xff1f; 首先header.html页面是被包含在r…

人力资源杂志人力资源杂志社人力资源编辑部2022年第20期目录

专题研究《人力资源》投稿&#xff1a;cnqikantg126.com 破解高端人才引聚难 林翠玲; 1-3 如何凸显集团企业人力资源管理价值 刘鹏飞; 4-5 中长期激励&#xff0c;激活科研人员创新力 刘灿;汪思源; 6-7 改革人才评价体系&#xff0c;助推“双一流”建设 杨帆; …

STM32CUBEMX_配置stm32f103c8t6的bootloader_USB升级

STM32CUBEMX_配置stm32f103c8t6的bootloader_USB升级 1、使用stm32cubemx配置好工程&#xff08;版本6.3.0&#xff09;&#xff0c;生成keil工程 2、移植必须要的一些函数 3、使用dfusedemo工具软件测试 ①配时钟外设 ②配ST-Link调试接口&#xff08;防止无法二次烧录程序&a…

理解Python的协程(Coroutine)

生成器(Generator) yield表达式的使用生产者和消费者模型yield from表达式协程(Coroutine) asyncio.coroutineasync/await总结参考链接 由于GIL的存在&#xff0c;导致Python多线程性能甚至比单线程更糟。 GIL: 全局解释器锁&#xff08;英语&#xff1a;Global Interpreter Lo…

InvocationException: GraphViz‘s executables not found【BUG已解决】

项目场景&#xff1a; 在机器学习框架中遇到了一个bug&#xff1a;安装了GraphViz模块后&#xff0c;在对着sklearn的决策树文档操作输出决策树模型结果时&#xff0c;还是爆出了如下的错误&#xff1a; 报错位置为&#xff1a; display(Image(graph.create_png())) graph.wr…

艾美捷低内毒素CpG ODN寡聚脱氧核苷酸全家族系列

免疫佐剂&#xff0c;是一类可以增强抗原免疫原性的免疫调节分子、化合物或大分子复合物。大多数佐剂可以通过启动先天免疫系统来辅助抗原应答。先天免疫系统通过模式识别受体&#xff08;pattern recognition receptor&#xff0c;PRR&#xff09;来感知各种微生物表达的病原体…

SQL通用语法及分类

目录 MySQL的启动和连接&数据模型 SQL通用语法及分类 Data Definition Language DDL - 数据库操作 DDL - 表操作 表操作 - 创建&&查询 表操作 - 数据类型 表操作-修改&&删除 DDL总结 Data Manipulation Language DML - 插入数据insert DML - 更…

高佣金流量卡上线

01 流量卡业务 常规的运营商套餐相对都比较贵&#xff0c;少则六七十一个月&#xff0c;多则几百块一个月&#xff0c;这样的套餐让运营商赚盆满钵满。 随着5G需求越来越多&#xff0c;其他第三方运营商也开始市场化营销&#xff0c;流量卡的CPA基本在60-200之间一张卡&…