Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Getting key press event for any key



  • Such an example of global hotkleys works perfectly: http://amin-ahmadi.com/2015/11/14/how-to-use-system-wide-hotkeys-in-your-qt-application/ and it is global, exactly what i need, but that seems to be limited, only one hotkey can be bind.

    How can i get info in my Qt app when some key is pressed and when it's released?



  • You can register more than global hotkey, for example this will register ctrl+alt+E, ctrl+alt+F and ctrl+alt+G from mainwindow.cpp:

    RegisterHotKey(reinterpret_cast<HWND>(winId()),100,MOD_CONTROL | MOD_ALT,'E');
    RegisterHotKey(reinterpret_cast<HWND>(winId()),101,MOD_CONTROL | MOD_ALT,'F');
    RegisterHotKey(reinterpret_cast<HWND>(winId()),102,MOD_CONTROL | MOD_ALT,'G');
    

    To catch them, use the same MainWindow::nativeEvent then you can test either on the hotkey identifer in wParam:

    if ((WM_HOTKEY == msg->message) && (100 == msg->wParam))
    {
        qDebug() << "got ctrl+alt+E";
        return true;
    }
    

    or you can identify them via their virtual keycode

    if ((WM_HOTKEY == msg->message) && ('G' == (UINT) HIWORD(msg->lParam)))
    {
        qDebug() << "got ctrl+alt+G";
        return true;
    }
    

    However, to detect when a global hotkey is pressed down and when it is released, this RegisterHotKey() will not help. Instead you will need to use another API call:

    SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, ::GetModuleHandle(NULL), 0);
    


  • @hskoglund Oh tnx, before creating this topic i thought that it is all about LPARAM, this holds info about which key in that message.

    But how can i convert that LPARAM back to 'E' or 'G' char? Or QString if it was 'Enter' or 'ALT' code?



  • It's all a throwback to the 80's (think Madonna etc.) WPARAM and LPARAM are the generic names for passing around stuff in Windows messages.
    To convert a LPARAM back to a Windows Virtual Key code:

    UINT vk = (UINT) HIWORD(msg->lParam);
    if ('E' == vk)
    // e key
    if (VK_RETURN == vk)
    // return key
    

    Note: 'ALT' key pressed by itself, i.e. not in combination with any other key, is not possible to detect using RegisterHotKey. The same goes for Left Shift or Left Control etc. To detect those guys you need to descend further into the Windows API, using SetWindowsHookEx(WH_KEYBOARD_LL,...);



  • @hskoglund Okay, i almost done that. Last thing is left - understand how can i put my function as a parameter to SetWindowsHookEx.

    Because this function is a class member - it gives me such an error if i try put it as a parameter:

    reference to non static member function must be called

    I even tried to do like this(myHook is my function):

    SetWindowsHookEx(WH_KEYBOARD_LL, &MainAppW::myHook, NULL, 0)
    

    But it also wrong. How should i pass my function as pointer there?


  • Lifetime Qt Champion

    @Engelard

    Hi
    It expects the call back to be either a global ( non class function) or that memeber function be
    a static one.
    It cannot use a normal member function.

    so MainAppW::myHook must be static or you must use a global/non member function.
    https://www.learncpp.com/cpp-tutorial/812-static-member-functions/
    DO read about static before just uisng it.
    It has restrictions due to being static.
    Like the function cannot use other not non-static members variables.



  • @mrjj forgot about that, tnx)

    Now when my myHook() static function is correct, everything is working. But i can't really interfere with any part of my app, since those all nonstatic.

    How can i from my static function, send any info to my ui->textBrowser for example?



  • Hi, I had the same problem last year when I tested SetWindowsHookEx, I created a small QObject-based helper class that had the callback as a static member function. And then to make that class reachable from inside the KeyboardProc(), I created a static instance of it in my mainwindow.cpp.
    The program is here:
    https://gitlab.com/tungware/app/-/tree/master/KeyboardHookTest


  • Qt Champions 2019

    @Engelard You can always add a static method to your class which returns a pointer to the instance (see singleton pattern).



  • @hskoglund Thank you for your reply, after i finished my version of this problem i viewed to yours. Frankly, it seems bit more complex then i expected) and some things like:
    alt text
    Why dont just type simple 8 in array parameters?)

    But i get the point anyway, i also tried do with signals at first, but it gave me obvious error ": call to non-static member function without an object argument" so i give up))
    Completely new for me was that thing:
    alt text
    Before i only emited signals in a simple way, like "gotKey(qc);" never had a thought that it could be called from the actual object.

    And here is how i resolved this, some might say it is foolish or dumb, but that's my first solution to this annoying STATIC issue, i get pretty perfect-looking result and i'm happy with it:

    In my worker class i created two static variables - bool eventOccured and KBDLLHOOKSTRUCT kbdStruct.
    bool initialized as FALSE, and worker class have only one function:

    void worker::threadStart()
    {
        while(true)
        {
            if(eventOccured)
            {
                sendStruct(kbdStruct);    // <---my signal
                eventOccured = false;
            }
        }
    }
    

    And here is my lovely STATIC function:

    LRESULT MainAppW::mySukaHook(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if(nCode>=0 && WM_KEYDOWN==wParam)
        {
            worker::kbdStruct = *(reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam));
            worker::eventOccured = true;
        }
        return CallNextHookEx(MainAppW::myHook, nCode, wParam, lParam);
    }
    

    that's it folks)


  • Lifetime Qt Champion

    @Engelard said in Getting key press event for any key:

    Why dont just type simple 8 in array parameters?)

    If you type 8 directly. then in another place, you also type 8 directly and then someday you might want 10
    and have to go hunt for all the 8 around in the code.
    So its always much better to use a const named variable instead of the raw value.



  • @hskoglund Eventually made as in your example, i only can't get one thing. What is better way to connect signals? In your program you did:

    connect(this,&KeyboardHooker::gotKey,pMainWindow,&MainWindow::keyFromHook);
    

    When i always use:

    connect(this, SIGNAL(sendStruct(tagKBDLLHOOKSTRUCT)), mWindow, SLOT(receiveStruct(tagKBDLLHOOKSTRUCT)));
    

    Tried to do your way, it gave me bunch of errors. What is the difference?



  • Hi, it's called the old and new syntax and the advantage with the new syntax (without SIGNAL/SLOT) is that most of the unexpected/nasty surprises happen at compile time instead of when you run the app. Which is a good thing :-)


Log in to reply