Davor Josipovic Just another WordPress blog – rather tryout

24/11/2010

System-wide events and Qt

Filed under: C++,Programming,Qt — Tags: , , — Davor @ 23:02

Few days back I wanted to create a macro recorder for the desktop. I though I could do this from within the Qt framework which has a neat high-level platform-independent event system that I thought I could incorporate with widgets to make a nice user interface. Well, that’s what I though at least… Truth is, Qt doesn’t offer any means for catching system-wide events. Eventually, I managed to put together a little command line application that can record and replay/loop the mouse and keyboard input messages based on the Windows API. What you will read next is my explanation why catching system-wide events is impossible from within the Qt framework, what the Windows API offers for this purpose, and a proposal on how system-wide events could be incorporated in the Qt event system.

Background

Windows and Qt applications are event driven, which means that they wait for the system to pass events to them upon which they can act. Simply put, those events are passed as “messages” to application windows through a callback function named the “window procedure”.

For example, when a user moves a mouse, the mouse device driver places the recorded movement in the system’s message queue. The system then determines the destination window and “posts” this message to the correct “thread message queue” (i.e. the message queue of the thread which created the destination window). Our Qt application has only access to these messages from its thread message queue. These messages are called “spontaneous events” in the Qt documentation. (Note: there exist also “sent” messages which bypass the message queue, but which we do not consider here.)

Now as one can see, the system is very selective on which mouse and keyboard events our Qt application will get. For example, our window will not get any keyboard messages if it is not in focus, nor will it get any mouse events if the mouse is not inside the window borders. So how can we access those events from the Qt framework? To my knowledge Qt doesn’t offer any ready made functions for this, so we will have to find some other way: directly accessing the Windows API.

Hooks

The Windows API offers a mechanism called a “hook” which we can hook somewhere in the message-handling mechanism and which will intercept and reroute the messages to some “hook procedure” we define. We define a hook with the API SetWindowsHookEx procedure. This procedure allows us to specify the “hook type” which defines the kind of messages to be intercepted (like WH_MOUSE_LL, WH_KEYBOARD_LL, WH_SHELL), the “hook scope” which can be thread or global (i.e. system), and the hook procedure with the following predefined signature:

 LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);

So to get you started here is a very basic application which uses the above described mechanism an which can be used as a starting point for building applications which are able to monitor system events.

#include <QtGui/QApplication>
#include <QDebug>
 
#include <windows.h>
 
LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    // process event...
    qDebug() << nCode << wParam << lParam;
 
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
 
    if (SetWindowsHookEx(WH_KEYBOARD_LL, HookProc, qWinAppInst(), NULL) == 0)
        qDebug() << "Hook failed for application instance" << qWinAppInst() << "with error:" << GetLastError();
 
    return a.exec();
}

For more information, the following links might be interesting:

I’ve put together a little command line utility that is able to record and replay/loop the recording based on the above explanation. Sourcecode is available here. To record: “winrec -record /f “recording.txt””. To play: “winrec -play /f “recording.txt” /repeat 0″. To stop any of these, just press Crtl+Esc.

I think integration with an Qt would probably be straightforward when used with QAbstractEventDispatcher, although I didn’t have time to test it.

11/08/2010

“Undefined reference to vtable” for QObject-derived classes in the implementation (.cpp) file.

Filed under: Programming,Qt — Tags: , — Davor @ 18:14

“undefined reference to vtable” seems to be a common problem with Qt’s meta-object system in combination with GCC.

The main cause due to GCC is described here: http://gcc.gnu.org/faq.html#vtables

But with QObject it gets a bit more complex. When you use the macro Q_OBJECT, you define some virtual methods which might trigger the above GCC error under certain circumstances. So make sure your use of the Q_OBJECT macro fulfills the following requirements:

  1. Make sure the Q_OBJECT macro is present in the definition of all QObject-derived classes
  2. Make sure you define your QObject-derived classes in your header files ONLY
  3. Make sure all of your header files are listed in your .pro file in the HEADERS-list
  4. Run qmake every time you add Q_OBJECT to one of your classes or modify your .pro file

(Source: http://www.theirishpenguin.com/2007/07/01/qobject-qmake-and-sadness-undefined-reference-to-vtable/)

Not fulfilling point 2:
You get the “undefined reference to vtable” error if you put your QObject-derived class in the implementation file because moc will not “add” the implementation of the virtual functions to the cpp file. Apparently you can enforce this by adding “#include cpp_file_name.moc” to your “cpp_file_name.cpp” file. Moc will detect this and will generate a “cpp_file_name.moc” file.

11/05/2010

IsDirty() class member for QDataWidgetMapper

Filed under: C++,Programming,Qt — Tags: , — Davor @ 15:57

Here is something I wanted to share. It’s a pity something similar isn’t implemented by the Trolls. But then again, it doesn’t require much coding either.

    bool isDirty() const {
        Q_ASSERT(orientation() == Qt::Horizontal);
        Q_ASSERT(rootIndex() == QModelIndex());
        for(int i = 0; i < model()->columnCount(); i++) {
            QWidget *mapWidget = mappedWidgetAt(i);
            if (mapWidget){
                QByteArray p = mappedPropertyName(mapWidget);
                QModelIndex idx = model()->index(currentIndex(), i);
                if (idx.data(Qt::EditRole) != mapWidget->property(p))
                    return true;
            }
        }
        return false;
    }

One use is to check whether an update is required before going to the next record…

PS Other way one could achieve the same thing by adjusting the source code because the “mappings”-container is in the private implementation of QDataWidgetMapper.

Powered by WordPress