Here the requirement is just to check if any key was pressed or any mouse click or movement has happened. Capturing other specific details is not required.
I am fine to do every 1 second polling as well to see if any mouse or key event happened.
Will QAbstractNativeEventFilter
be of any help?
Any other platform independent C++ library also will be useful.
Below is a sample code to capture mouse & keyboard events only when the app is in focus:
#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();
}
Have written minimal working code for the 3 platforms below. Using Qt is optional.
#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: https://stackoverflow.com/questions/69711608/why-xrecorddisablecontext-is-not-working
// ::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
// https://stackoverflow.com/questions/4556278/cgeventtapcreates-watching-keyboard-input-in-cocoa
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