I'm wondering how can I write a code to monitor the mouse buttons globally. This would be for OS X, and I'd like to try writing it in Qt/C++.
To begin with I don't know how to capture those global events. The monitor application would not display a GUI, it'd simply be a process that runs in the background and detects mouse buttons being clicked.
In the second part of the program I would like to launch hot-keys depending of the mouse key pressed.
My final idea is make a free program like steerMouse, just to figure out how it could be done.
I'm asking for a guidance of where to start - how can I detect the mouse button events globally?
It's not possible using only Qt. There's another question that details the issues. It boils down to:
Installing an event filter on QApplication
will let you receive mouse events while the cursor is over any application window, but not outside of it. That's not helpful in your case.
If a widget grabs the mouse using grabMouse()
, it will receive all mouse events globally, but interaction with other applications becomes impossible.
So, you'll need to resort to using platform-specific APIs to do this - that means Cocoa and writing in Objective C/C++. There's a question with excellent answers that provides almost everything we need but Qt integration.
The missing part, shown below, is integrating the stand-alone code with Qt. This code shows an empty widget just to demonstrate that we correctly handle mouse events for both our application, and outside of it.
This is a complete, working example, using Cocoa. It needs to go into a .mm
file; don't forget to add it to OBJECTIVE_SOURCES
in your qmake project file (not to SOURCES
!).
Unfortunately, there's isn't a single function/method that would translate from NSEvent
to QMouseEvent
. The best one can do is copy&paste some code from qnsview.mm
. This is unfortunate but results from the design of Qt platform abstraction: the platform code ends up calling QWindowSystemInterface::handleMouseEvent(....)
to post the event to the application.
#include <QApplication>
#include <QAbstractNativeEventFilter>
#include <QTextStream>
#include <QWidget>
#include <cstdio>
#import <AppKit/AppKit.h>
QTextStream out(stdout);
class MyEventFilter : public QAbstractNativeEventFilter {
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) {
Q_UNUSED(eventType) Q_UNUSED(result)
NSEvent * event = (NSEvent*)message;
switch ([event type]) {
case NSLeftMouseDown:
out << "Lv"; break;
case NSLeftMouseUp:
out << "L^"; break;
case NSRightMouseDown:
out << "Rv"; break;
case NSRightMouseUp:
out << "R^"; break;
case NSOtherMouseDown:
out << [event buttonNumber] << "v"; break;
case NSOtherMouseUp:
out << [event buttonNumber] << "^"; break;
default:
return false;
}
out << endl;
return false;
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSharedPointer<QAbstractNativeEventFilter> filter(new MyEventFilter);
const int mask =
NSLeftMouseDownMask | NSLeftMouseUpMask |
NSRightMouseDownMask | NSRightMouseUpMask |
NSOtherMouseDownMask | NSOtherMouseUpMask;
// The global monitoring handler is *not* called for events sent to our application
id monitorId = [NSEvent addGlobalMonitorForEventsMatchingMask:mask handler:^(NSEvent* event) {
filter->nativeEventFilter("NSEvent", event, 0);
}];
// We also need to handle events coming to our application
a.installNativeEventFilter(filter.data());
QWidget w;
w.show();
int rc = a.exec();
[NSEvent removeMonitor:monitorId];
return rc;
}