即使 Qt 应用程序最小化/在后台/失焦,如何捕获键盘和鼠标事件?

iam*_*ind 1 c++ qt keypress capture mouseevent

这里的要求只是检查是否按下了任何键或发生了任何鼠标单击或移动。不需要捕获其他具体细节。
我也可以每隔 1 秒进行一次轮询,看看是否发生了任何鼠标或按键事件。

会有QAbstractNativeEventFilter什么帮助吗?
任何其他独立于平台的 C++ 库也将很有用。


下面是仅当应用程序处于焦点时捕获鼠标和键盘事件的示例代码:

#include<QApplication>
#include<QDebug>
#include<QKeyEvent>
#include<QWidget>

struct Widget : public QWidget
{
  Widget ()
  {
    installEventFilter(this);
    grabKeyboard();
    grabMouse();
    setMouseTracking(true);
  }
  ~Widget () { qDebug() << "~Event()"; }

  bool eventFilter (QObject* const pObject,
                    QEvent* const pEvent) override
  {
    qDebug() << "Event: " << pEvent->type();
    if(pEvent->type() == QEvent::KeyPress)
    {
      QKeyEvent* const pKeyEvent = static_cast<QKeyEvent*>(pEvent);
      qDebug() << "Key event: " << pKeyEvent->key();
    }
    return false; //QObject::eventFilter(pObject, pEvent);
  }
};


int main (int argc, char *argv[])
{
  QApplication application(argc, argv);
  Widget widget;
  widget.show();
  return application.exec();
}
Run Code Online (Sandbox Code Playgroud)

iam*_*ind 6

已为以下 3 个平台编写了最少的工作代码。使用 Qt 是可选的。

#include<iostream>
#include<thread>
#include<chrono>

struct Activity
{
  Activity ();
};

// You may need to put Qt equivalent part here; The core logic is after main()
#include<QGuiApplication>
int
main (int argc, char *argv[])
{  // Qt specific; but it can be anything here
  QGuiApplication application(argc, argv);
  Activity activity;
  return application.exec();
}

#ifdef __linux__  // add in .pro file "linux: QT += x11extras" and "linux: LIBS += -lX11 -lXtst"
#include<X11/Xlib.h>
#include<X11/extensions/record.h>

#define CHECK(EVENT) if(*pDatum == EVENT) std::cout << #EVENT
void Handle (XPointer, XRecordInterceptData *pRecord)
{
  using XRecordDatum = char;
  std::cout << pRecord->category << "---" << pRecord->data;
  if(auto* const pDatum = reinterpret_cast<XRecordDatum*>(pRecord->data))
  { CHECK(KeyPress); else CHECK(KeyRelease); else CHECK(ButtonPress); else CHECK(ButtonRelease); }
  ::XRecordFreeData(pRecord);
}

Activity::Activity ()
{
  if(auto* const pDisplay = XOpenDisplay(nullptr))
  {
    XRecordClientSpec clients = XRecordAllClients;
    auto* pRange = ::XRecordAllocRange();
    pRange->device_events = XRecordRange8{KeyPress, ButtonRelease};
    auto context = ::XRecordCreateContext(pDisplay, 0, &clients, 1, &pRange, 1);
    ::XRecordEnableContextAsync(pDisplay, context, Handle, nullptr); // use with/without `...Async()`

    //  ::XRecordProcessReplies(pDisplay);
    ::XFlush(pDisplay);
    ::XSync(pDisplay, true);
  }
// Use below functions by putting variables in global scope to stop capturing
// Also refer: /sf/ask/4879812591/
// ::XRecordDisableContext(pDisplay, context);
//  ::XRecordFreeContext(pDisplay, context);
//  ::XFree(pRange);
}
#endif

#ifdef WIN32
#include<Windows.h> // add in .pro file "win32: LIBS += -luser32"

HHOOK sHookKeyboard, sHookMouse;
Activity::Activity ()
{
    sHookKeyboard = ::SetWindowsHookExA(WH_KEYBOARD_LL,
                     [] (int i, WPARAM w, LPARAM l)
                     { std::cout << i << w << l; return ::CallNextHookEx(sHookKeyboard, i, w, l); },
                                        0, 0);
    sHookMouse = ::SetWindowsHookExA(WH_MOUSE_LL,
                     [] (int i, WPARAM w, LPARAM l)
                     { std::cout << i << w << l; return ::CallNextHookEx(sHookMouse, i, w, l); },
                                        0, 0);
}
#endif

#ifdef __APPLE__ // add in .pro file "mac: LIBS += -framework ApplicationServices"
#include<ApplicationServices/ApplicationServices.h>
// Add binary (& if you are it via running Qt, then that too) into the "privacy" settings of the ...
// ... Mac system; System Preferences > Privacy settings; Without this the code will not work

// https://developer.apple.com/forums/thread/109283
// /sf/ask/318939491/
Activity::Activity ()
{
  CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventScrollWheel) |
                     CGEventMaskBit(kCGEventLeftMouseDown) | CGEventMaskBit(kCGEventRightMouseDown);
  auto eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap,
                                   kCGEventTapOptionDefault, mask,
                       [] (CGEventTapProxy, CGEventType type, CGEventRef event, void*)
                       { std::cout << "captured: " << type; return event; }, this);
  if(eventTap == nullptr)
    return;

  CFRunLoopAddSource(CFRunLoopGetCurrent(),
                     CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
                     kCFRunLoopCommonModes);
  // Enable the event tap.
  CGEventTapEnable(eventTap, true);
}
#endif
Run Code Online (Sandbox Code Playgroud)