Grabbing keyboard input in Qt

I’m currently writing an editor for our next game. We’ve used many tool in the past (Blender, Tiled & Mappy) to make games but we thought it was really time to invest in our own code base. This was heavily influenced by the great experience we had using Unity3D to prototype our next game.

image

I think you can see how much we were influenced by Unity3D in this shot.

Anyway, to write the editor I turned to Qt as a reliable cross platform library. I wanted to be able to play the game IN the editor so we could get some of the quick turn around for tweaking that you get in Unity3D.

The editor already uses SFML to read controllers and exposes this input to the Lua scripting system. However the SFML keyboard code (sf::keyboard) does not work because it’s not hooked in to the applications event loop as Qt owns (and basically hides away) the event loop.

To work around this I learned that you can install an Event Filter in the main application QWidget and filter out/redirect keyboard events. Using this I can stop keyboard short cuts from changing the editor state and pass the key presses to the Lua scripting system.

The Code.

So when the user presses the play button on the toolbar the code installs the Event Filter

1
QApplication::instance()->installEventFilter(mEventFilter);

and when the game stops, the user presses pause or stop the Event Filter is removed

1
QApplication::instance()->removeEventFilter(mEventFilter);

The event filter itself is pretty simple:

1
2
3
4
5
6
class EventFilter : public QObject{
    Q_OBJECT
public:

    virtual bool eventFilter(QObject *object, QEvent *event)override;
};

with the main actual Filter function looking like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
bool EventFilter::eventFilter(QObject */*object*/, QEvent *event){

    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        voxity::RegisterKeydown(QtKeyToSFML(keyEvent->key()));

        return true;
    }

    if (event->type() == QEvent::KeyRelease) {
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        voxity::RegisterKeyup(QtKeyToSFML(keyEvent->key()));
        return true;
    }

    if(event->type() == QEvent::Shortcut){
        QShortcutEvent *sc = static_cast<QShortcutEvent *>(event);
        const QKeySequence &ks = sc->key();

        voxity::RegisterKeydown(QtKeyToSFML(sc->key()[0]));
        return true;
    }

    return false;
}

If the filter function returns true then the event should be ignored by the rest of the application and the event is discarded. Otherwise it carries on it’s way.

The Qt docs talk about filter functions slowing down the app but it doesn’t seem to make much difference to our editor. It’s only installed when the game us running anyway so most of the load is in running the game.

Basically if it’s a keypress/keyrelease event the RegisterKeydown or RegisterKeyup is called. This records the keypress in a way that the Lua scripting system can pick up that a key is down or up. The Qt keycode is converted to an SFML code as the game runtime (when it running outside of the editor) will just be using SFML.

The one quirk is if the event is a Shortcut. The editor has many single keys that map to actions (for example w activates translate mode and e activates rotate mode). These come through as shortcuts NOT keypresses. This hints that there is a lot of code beneath this filter playing with the raw input. For the initial keypress of a shortcut a Shortcut event is triggered but the release of the key comes through as a KeyRelease event.

Not Perfect Though

This scheme doesn’t seem to intercept CMD-Q, CMD-P etc events that are assumed by all apps to be Quit and Print. Further investigation may reveal how they can be intercepted but I don’t need to grab them at the moment.

I hope this helps someone.