0

I have added an override for my main widget's keyPressEvent:

void MainWindow::keyPressEvent(QKeyEvent* e)
{
    if (e->key() == Qt::Key_F11)
    {
        if (e->modifiers() & Qt::AltModifier && e->modifiers() & Qt::ControlModifier)
        {
            // do stuff
        }
    }
}

The problem is that it does not work. If I try the AltModifier or ControlModifier alone it works (while changing the second condition of course), but not for both of them. The key() won't equal to Qt::Key_F11 while I do press F11. I'm using windows.

Edit: checked with logging and the result is that Ctrl+Alt+F11 and Ctrl+Alt+F12 do not send a key event (while other Ctrl+Alt+Fxx keys do).

Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180
  • Do you mean with both control and alt keys down, pressing F11 doesn't set event key to Key_F11? – Stephen Chu Mar 28 '11 at 18:07
  • So what is e->key() in that case? – Matt Phillips Mar 28 '11 at 21:10
  • Also, how can it "do the job well" if only one modifier is pressed? In that case your nested if (...) test should return false. Or do you just mean that you do get e->key() == Qt::Key_F11 in that case. – Matt Phillips Mar 28 '11 at 21:15
  • 1
    In that case I changed the second condition to see if it works. It works with F11+Alt and F11+Control but not F11+Alt+Control. For F11+Alt+Control the returned value of `e->key()` is 16777249 (more precisely, it is the value of the `k` field in the QKeyEvent object). This value is consistently the same. The value of Qt::Key_F11 is 16777274. – Tamás Szelei Mar 28 '11 at 23:10
  • @Tamas Szelei so what's the value of Ctrl+Alt+k? Also, worst case scenario: #ifdef – Matt Phillips Mar 29 '11 at 03:09
  • Ctrl+Alt+k gives the code 16777249. What would I check with #ifdef? – Tamás Szelei Mar 29 '11 at 08:56
  • @Tamas Szelei I meant just use #ifdef to create a Windows-specific version of your code. But now I'm really confused--16777249 (0x1000021) is the value for Qt::Key_Control, not 'k'. – Matt Phillips Mar 29 '11 at 12:11
  • You are right, it is indeed Qt::Key_Control. – Tamás Szelei Mar 29 '11 at 12:17
  • @Tamas Szelei Just to double-check--are you really sure that when Alt and Ctrl are down, you do *not* receive a Qt::Key_F11 event? The output of qDebug() << e->key() is 0x1000021 when, in that situation, F11 is pressed? If so then sorry all I can say is try QtCentre. – Matt Phillips Mar 29 '11 at 12:21
  • I checked with logging and the result is that Ctrl+Alt+F11 and Ctrl+Alt+F12 does not send a key event (while other Ctrl+Alt+Fxx keys do). Getting the control keycode was because I pressed that first and used a breakpoint to see what's going on, and the program was already paused while I pressed the other keys. I'll try QtCentre, thank you for your time. – Tamás Szelei Mar 29 '11 at 12:51
  • I managed to find a solution, see my answer below if you are interested. – Tamás Szelei Mar 29 '11 at 14:31

1 Answers1

2

Oook, so I managed to solve it though I'm not exactly pleased with the solution. At least there is no mystery and it works :).

The reason why I didn't receive the hotkeys Ctrl+Alt+F11 and Ctrl+Alt+F12

They were registered as global hotkeys. I managed to find this out using the ActiveHotkeys program of the fellow stackoverflow member moodforaday (thanks a lot for it!). Apparently there is no documented way to find out which program registered a particular hotkey (and it didn't do anything on my system). See moodforaday's thread about the issue.

The solution

One of the answers in the aforementioned thread led me to another question. Efotinis' answer was absolutely perfect for me. I did not have experience with setting up low-level keyboard hooks, but it was not nearly as difficult as it sounds. For future's sake, here is how I did it in my Qt application:

In my mainwindow.h:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    // ... code

private:    
    void tryLogin();
    friend LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);

};

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);

In my mainwindow.cpp:

// setting up the hook in the constructor
SetWindowsHookEx(WH_KEYBOARD_LL,
                     LowLevelKeyboardProc,
                     NULL,
                     0);

The hook code (mostly from efotinis' answer):

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam)
{
    KBDLLHOOKSTRUCT* kllhs = reinterpret_cast<KBDLLHOOKSTRUCT*>(lparam);
    if (code == HC_ACTION)
    {
        if (wparam == WM_KEYDOWN && kllhs->vkCode == VK_F12 &&
            (GetAsyncKeyState(VK_MENU) < 0 && GetAsyncKeyState(VK_CONTROL) < 0))
        {
            MainWindow* w = dynamic_cast<MainWindow*>(qApp->activeWindow());
            if (NULL != w)
            {
                w->tryLogin();  // this should not be blocking!
                return 1;
            }
        }
    }

    return CallNextHookEx(0, code, wparam, lparam);
}

As you can see, we get the pointer to the application window from the global QApplication object. We use dynamic_cast so in the active window happens to not be a MainWindow instance we would get a NULL pointer.

If you are wondering why the GetAsyncKeyState calls are checked for < 0, it's because this function returns with MSB set if the key is down. And when the MSB is set, the SHORT number is negative (on x86/x64 and compatible platforms). If windows ever gets ported to a platform where signed integers are represented differently, this code might break. The absolutely proper way would be to create a 16-bit mask and use that to check the MSB, but I'm lazy to do that. :)

One thing to note is that when you call a function from your hook, the Qt event loop has just begun processing. That means until you don't return from your function, it will block the UI (freezing it for a few seconds). If you wanted to show a dialog like I did, instead of exec(), call raise, activateWindow and show, while setting the window modality of the dialog to modal (in its constructor maybe).

You can unregister the hook with UnHookWindowsHookEx if you want to (which happens when the module containing the hook is unloaded). To do this, save the return value of the SetWindowsHookEx call.

Community
  • 1
  • 1
Tamás Szelei
  • 23,169
  • 18
  • 105
  • 180