How do I use QKeyEvent::key() even if window is not on top?



  • Hello there,

    I've been developing a sort of code manager where functions are set and activated by hex codes (It's pretty much an esoteric programming language with an actual use lol).
    Right now I'm coding a function to check if a certain key is pressed in order to enable the following instruction(s).
    i can already get the current key's value when the window is on top. But it stops working as soon as I leave the window.
    I need it to be active while I'm outside the window or in the actual game.

    For testing purposes I'd like to get this code running while the window is not on top.

    void MainWindow::keyPressEvent(QKeyEvent *ckey)
    {
        ui->label->setText(QString::number(ckey->key(), 16) + " " + ckey->text() + " " + QString::number(ckey->nativeScanCode(), 16));
    }
    

    The application also has a QTimer that is permanently active even if the window is not on top. Maybe this can be used as a reference point here...

    Would anyone like to help me?
    Thanks


  • Moderators

    @SnuggleKat said in How do I use QKeyEvent::key() even if window is not on top?:

    I need it to be active while I'm outside the window or in the actual game.

    then Qt is not the right framework for you.
    By definition only the active GUI window gets the input events delivered.

    You need to hook into the OS using native API to achieve what you want.



  • Hi @SnuggleKat

    What if you install an eventFilter ?

    There is an example in the link:

    class KeyPressEater : public QObject
    {
        Q_OBJECT
        ...
    
    protected:
        bool eventFilter(QObject *obj, QEvent *event);
    };
    
    bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
    {
        if (event->type() == QEvent::KeyPress) {
            QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
            qDebug("Ate key press %d", keyEvent->key());
            return true;
        } else {
            // standard event processing
            return QObject::eventFilter(obj, event);
        }
    }
    

    And here's how to install it on two widgets:

    KeyPressEater *keyPressEater = new KeyPressEater(this);
    QPushButton *pushButton = new QPushButton(this);
    QListView *listView = new QListView(this);
    
    pushButton->installEventFilter(keyPressEater);
    listView->installEventFilter(keyPressEater);
    


  • @raven-worx said in How do I use QKeyEvent::key() even if window is not on top?:

    then Qt is not the right framework for you.
    By definition only the active GUI window gets the input events delivered.

    You need to hook into the OS using native API to achieve what you want.

    I think there is a way to achieve this with Qt (and an API) since there is other programs developed with Qt that allow "Background Input" (e.g. Dolphin Emulator).

    I'm checking out Microsoft's API Documentation right now but I'm uncertain which Function is the right one: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645530(v=vs.85).aspx


  • Moderators

    @SnuggleKat said in How do I use QKeyEvent::key() even if window is not on top?:

    I think there is a way to achieve this with Qt (and an API)

    thats what i said...
    Qt alone isn't capable of it. As i said you need to use native API to get the key events.
    Of course you can create artificial events at any time and inject them into Qt's event-loop.


  • Qt Champions 2017



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    Hi
    it sounds like you want a global hot key
    https://msdn.microsoft.com/en-us/library/windows/desktop/ms646309(v=vs.85).aspx

    This looks like what I need. Thanks!
    Using this results in 2 error messages:

    - mainwindow.obj:-1: Fehler: LNK2019: unresolved external symbol __imp_RegisterHotKey referenced in function "public: void __cdecl MainWindow::timer(void)" (?timer@MainWindow@@QEAAXXZ)
    - debug\Code_Manager.exe:-1: Fehler: LNK1120: 1 unresolved externals
    

    I have tried checking whether the b-key is pressed:

        if (RegisterHotKey(NULL, 1, MOD_ALT | MOD_NOREPEAT, 0x42) == TRUE)  //0x42 is 'b'
        {
            ui->label->setText("true");
        }
        else
        {
            ui->label->setText("false");
        }
    

  • Qt Champions 2017

    @SnuggleKat
    Hi
    The trick is always check which library to link to
    alt text

    Then add

    LIBS += -luser32

    to the .pro file.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    LIBS += -luser32

    Thanks, but the problem still remains the same


  • Qt Champions 2017

    @SnuggleKat
    Remember to run qmake after adding it
    Up in the build menu.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Remember to run qmake after adding it
    Up in the build menu.

    Thanks. I could compile it now. But the key value doesn't get checked. Could this issue be caused by the function being inside called up by a QTimer?
    If I put the function into the MainWindow::MainWindow the RegisterHotKey permanently returns TRUE


  • Qt Champions 2017

    Hi
    You mean from a timer slot ?
    Should not make a difference however if you call it multiple times t without -
    UnregisterHotKey , it might considered it already registered and return false.
    you can show what GetLastError says in that case.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    Hi
    You mean from a timer slot ?
    Should not make a difference however if you call it multiple times t without -
    UnregisterHotKey , it might considered it already registered and return false.
    you can show what GetLastError says in that case.

    Well, I got it working while the window is on top. But once I leave the window the button isn't registered anymore.

    RegisterHotKey(NULL, 0, MOD_IGNORE_ALL_MODIFIER, 0x42);
        MSG msg;
        GetMessage(&msg, 0, 0, 0);
        PeekMessage(&msg, 0, 0, 0, 0x0001);
    
        if(msg.wParam == 0x42)  //0x42 is 'b'
        {
            ui->label->setText("true");
            *(cheat_array + 16) = 0x11;
        }
        else
        {
            ui->label->setText("false");
            *(cheat_array + 16) = 0x00;
        }
    

  • Qt Champions 2017

    Hi
    You are using NULL for windows handle
    Docs says
    "If this parameter is NULL, WM_HOTKEY messages are posted to the message queue of the calling thread and must be processed in the message loop."

    So i am wondering if it gets delivered by Qt when its not active.

    Try to use its
    (HWND)winId();
    instead of NULL




  • Qt Champions 2017

    @SnuggleKat
    Ok, i think its related to how the events are sent when windows not
    in focus.

    try use eventfilter for native events and see if it comes there when not in focus.
    https://forum.qt.io/topic/32640/help-with-qabstractnativeeventfilter

    you can also try this sample ( non qt) and see if that works in all cases.
    Its uses native msg pump.

    #include <stdio.h>
    #include <tchar.h>
    #include <windows.h>
    
    int main()
    {       
    	enum{ONE_KEYID = 1, TWO_KEYID = 2};
    	RegisterHotKey(0, ONE_KEYID, MOD_NOREPEAT, 0x31); // register 1 key as hotkey
    	RegisterHotKey(0, TWO_KEYID, MOD_NOREPEAT, 0x32); // register 2 key as hotkey
    	MSG msg;
    	while(GetMessage(&msg, 0, 0, 0))
    	{
    		PeekMessage(&msg, 0, 0, 0, 0x0001);
    		switch(msg.message)
    		{
    		case WM_HOTKEY:
    			if(msg.wParam == ONE_KEYID)
    			{
    				printf("1 Pressed");
    			}
    			else if(msg.wParam == TWO_KEYID)
    			{
    				printf("2 Pressed");
    			}
    		}
    	}
    	return 0;
    }
    


  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Ok, i think its related to how the events are sent when windows not
    in focus.

    try use eventfilter for native events and see if it comes there when not in focus.
    https://forum.qt.io/topic/32640/help-with-qabstractnativeeventfilter

    you can also try this sample ( non qt) and see if that works in all cases.
    Its uses native msg pump.

    #include <stdio.h>
    #includ...
    

    My recent code update was based on this example.
    Tried rebuilding it but didn't work either.

    And I'm a little bit lost about the EventFilter thing..


  • Qt Champions 2017

    @SnuggleKat

    The link have sample of such eventfiler.
    its not much code.

    Its odd, the other sample didnt work as its windows less and have message pump.
    But if u mix with a normal Qt it makes sense as the events are eating by QApplication then.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat

    The link have sample of such eventfiler.
    its not much code.

    Its odd, the other sample didnt work as its windows less and have message pump.
    But if u mix with a normal Qt it makes sense as the events are eating by QApplication then.

    I'm sorry, I can't get it to compile..

    Could it be it doesn't work because the timer() function is a (public) slot?


  • Qt Champions 2017

    @SnuggleKat
    A slot is just a normal c++ function.

    But i dont understand why you need to call it in a timer.
    You normally call it ONLY once to set up hot key.
    Then reacts to WM_HOTKEY native event.
    There would be no need of a timer.

    Mixing Qt application and GetMessage() might have side effects.

    This might help
    http://amin-ahmadi.com/2015/11/14/how-to-use-system-wide-hotkeys-in-your-qt-application/



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    A slot is just a normal c++ function.

    But i dont understand why you need to call it in a timer.
    You normally call it ONLY once to set up hot key.
    Then reacts to WM_HOTKEY native event.
    There would be no need of a timer.

    Mixing Qt application and GetMessage() might have side effects.

    This might help
    http://amin-ahmadi.com/2015/11/14/how-to-use-system-wide-hotkeys-in-your-qt-application/

    I need it in a timer because it executes the codehandler as many times a second as defined by the user.
    However, I could also transfer the key_value or key_code over to the timer


  • Qt Champions 2017

    Ok. but try the sample i linked. it is using virtual override for native Event and it might work when app is not focused.
    Else an event filter on application seems the best way to grab WM_HOTKEY.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    Ok. but try the sample i linked. it is using virtual override for native Event and it might work when app is not focused.
    Else an event filter on application seems the best way to grab WM_HOTKEY.

    Wow, it works, thank you!
    For some reason it makes the b-key not typing anything. is there are workaround?


  • Qt Champions 2017

    @SnuggleKat
    Super :)
    Some other app might have requested a hotkey on b.
    So if be is not sent to you. it means something else takes it.
    I think F12 also reserved and some others.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Super :)
    Some other app might have requested a hotkey on b.
    So if be is not sent to you. it means something else takes it.
    I think F12 also reserved and some others.

    I see!
    Well, it's not too bad though. I will upload a demo once I have made some more progress with my application


  • Qt Champions 2017

    @SnuggleKat
    Super. please mark as solved if possible.
    You can always open other if later questions comes up.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Super. please mark as solved if possible.
    You can always open other if later questions comes up.

    Here's a little demo video!
    https://twitter.com/CosmoCortney/status/929459711388344322

    Well, there came up another problem tho..
    when I pass another key value to the RegisterHotKey() function any of the both values trigger both functions at the same time.
    I think I should un-register the value after it's corresponding function was executed. but how do I do this?


  • Qt Champions 2017

    @SnuggleKat
    Hehe cool. Not sure what the reindeer is doing though :)

    Hi
    If you register 2 hotkeys. it will call nativeEvent twice regardless of which
    key you press or what do u mean ?

    if you read the docs, it says
    "lParam
    The low-order word specifies the keys that were to be pressed in combination with the key specified by the high-order word to generate the WM_HOTKEY message. This word can be one or more of the following values. The high-order word specifies the virtual key code of the hot key."

    So maybe you just need to alter the code and only call your function depending on what actual hot key is?

    Like

    #define VK_M 0x4D
    #define VK_N 0x4E
    
    MainWindow::MainWindow(QWidget* parent) :
      QMainWindow(parent),
      ui(new Ui::MainWindow) {
      ui->setupUi(this);
    
      if(!RegisterHotKey(HWND(winId()), 0, MOD_ALT | MOD_CONTROL, VK_M)) {
        QMessageBox::warning(this, "Warning", "Can’t register hotkey ALT + CTRL + M");
      }
    
      if(!RegisterHotKey(HWND(winId()), 0, MOD_ALT | MOD_CONTROL, VK_N)) {
        QMessageBox::warning(this, "Warning", "Can’t register hotkey ALT + CTRL + N");
      }
    }
    
    bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, long* result) {
      MSG* msg = static_cast<MSG*>(message);
      if(msg->message == WM_HOTKEY) {
        WORD hotKey =  HIWORD(msg->lParam);
        switch (hotKey) {
          case VK_M:
            QMessageBox::about(this, "", "VM_M pressed");
            break;
          case VK_N:
            QMessageBox::about(this, "", "VM_N pressed");
            break;
          default:
            break;
        }
        return true;
      }
      return false;
    }
    


  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Hehe cool. Not sure what the reindeer is doing though :)

    Hi
    If you register 2 hotkeys. it will call nativeEvent twice regardless of which
    key you press or what do u mean ?

    if you read the docs, it says
    "lParam
    The low-order word specifies the keys that were to be pressed in combination with the key specified by the high-order word to generate the WM_HOTKEY message. This word can be one or more of the following values. The high-order word specifies the virtual key code of the hot key."

    So maybe you just need to alter the code and only call your function depending on what actual hot key is?

    Thanks!
    What i did was loading the reindeer's x, y and z size values (floats) and incremented it and stored it back to the game each time the timer repeated. This action was triggered by the hotkey =)

    I have modified the code a lot in order to work as I need it:

    void MainWindow::if_key_pressed() // 080000XX 000000VV // this is what the cheat code layout looks like. VV = hotkey value
                                   // D00000XX BADF000D // this is the end-if code where to branch when the hot key is not pressed.
    {  
     key_value = *(cheat_array + index_B + 0x07);
        ifnr = *(cheat_array + index_B + 0x03);
    
    
        RegisterHotKey(HWND(winId()), 0, 0, key_value); // register hotkey
    
        if(key_is_pressed == false)
        {
            index_B += 0x08;
            for(int i = 0, j = index_B; (*(cheat_array + j + i) != 0xD0) || (*(cheat_array + j + i + 3) != ifnr); i += 8)
            {
                index_B += 8;
            }
        }
        else
        {
            index_B += 0x08;
        }
    }
    
    //....
    
    bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
    {
        Q_UNUSED(eventType);
        Q_UNUSED(result);
        MSG *msg = static_cast<MSG*>(message);
        if(msg->message == WM_HOTKEY) // check if key is pressed
        {
            key_is_pressed = true;
            true;
        }
        else
        {
            key_is_pressed = false;
            false;
        }
        return false;
    }
    
    
    // if the key is pressed index_B will jump to the cheats to be executed (e.g. to grow the deer). if it's not pressed it jumps to the end-if to skip the cheats
    

    so.. should I try clear msg?


  • Qt Champions 2017

    Hi
    ok. not sure what question is ?
    code will do the same regardless of what you register as hotkey.
    Is that what you wanted ?



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    Hi
    ok. not sure what question is ?
    code will do the same regardless of what you register as hotkey.
    Is that what you wanted ?

    Sorry for my absence, I had a busy month...
    The problem is that once the key was pressed key_is_pressed stays true even when I release the key. So the function I call up with the pressed key keeps being active.
    This is what I'd need to have fixed so I can control the flow of the code with the keys


  • Qt Champions 2017

    @SnuggleKat
    Ok try my code. did not notice it always being true.
    You register the shortcut on each key press. Its not how its ment to work.
    (i call it once in constructor)

    Also you must use
    WORD hotKey = HIWORD(msg->lParam);
    and not lParam directly.



  • @mrjj said in How do I use QKeyEvent::key() even if window is not on top?:

    @SnuggleKat
    Ok try my code. did not notice it always being true.
    You register the shortcut on each key press. Its not how its ment to work.
    (i call it once in constructor)

    Also you must use
    WORD hotKey = HIWORD(msg->lParam);
    and not lParam directly.

    Thanks,
    I had to modify it a little bit because the user can define which key to be checked (case labels don't like variables).

    bool MainWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
    {
        Q_UNUSED(eventType);
        Q_UNUSED(result);
    
        MSG *msg = static_cast<MSG*>(message);
    
        if(msg->message == WM_HOTKEY) // check if key is pressed
        {
            WORD hotKey =  HIWORD(msg->lParam);
            if(hotKey == key_value)
            {
                key_is_pressed = true;
                true;
                setWindowTitle("pressed");
            }
            else
            {
                key_is_pressed = false;
                false;
                setWindowTitle("not pressed");
            }
        }
        return false;
    }
    

    It behaves weird now.
    If I'm checking a certain user defined key value the function will stay on once the corresponding key was hit. (same as before)
    If I'm checking for more than one value where each value triggers a different function the earlier noted values will turn off everything and the last one will turn all functions on.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.