Detecting IF a key is being pressed
-
Another update: I think this problem is even worse and there seems to be some bug in QT looking more deeply into it
After you stop dragging the window, if you press the same key again, it won't register as a key press, but it will register as a key release once you raise the key... (and everything is then consistent again) so this means QT's internal state must be wrong since it seems its also waiting for the key release, which already happened while it was blocked
Update 2: Tried on OSX , and it also doesn't work there , so thinking this might be some QT specific bug
Update 3:
Here is the most minimal code example I could make to reproduce the issue, if you try to move or resize the widget while pressing any keyboard button , the event will never be called:
class KeyPressFilter : public QObject { public: bool eventFilter(QObject* aObject, QEvent* aEvent) final { if (aEvent->type() != QEvent::KeyRelease) return QObject::eventFilter(aObject, aEvent); if (!static_cast<QKeyEvent*>(aEvent)->isAutoRepeat()) { std::cout << "Will it be called? Who knows!" << std::endl; } return true; } }; int main(int aArgc, char *aArgv[]) { QApplication myApplication{aArgc, aArgv}; KeyPressFilter myKeyFilter; myApplication.installEventFilter(&myKeyFilter); QWidget myWidget; myWidget.show(); return myApplication.exec(); }
Could one of the QT pros give some insight on this? Even if it's just answering I should make a QT bug ticket (if it is one?)
Or even if the issue can be reproduced
-
This is while it still has focus, and infact I mark all keys as not pressed when
QEvent::WindowDeactivate
is receivedTo reproduce on Windows 10 for example you would:
- Hold 'W' key (for example), observe keyPressedEvent is called
- Start dragging the window while still holding W key, with your mouse. By dragging the window I mean clicking and holding on the window title bar and moving the mouse
- Release the 'W' key while still dragging. Observe the keyReleaseEvent is not called while this occurs.
- You will therefore get into some state with the window focused, and the application thinks the 'W' is still pressed, but it is not
Hi,
First, here's my exact environment:
- System :
- Operating System: KDE neon 5.27
- KDE Plasma Version: 5.27.5
- KDE Frameworks Version: 5.106.0
- Qt Version: 5.15.9
- Kernel Version: 5.19.0-41-generic (64-bit)
- Graphics Platform: X11
- Developing tools :
- Qt 6.5.0
- CMake 3.22.1
- Qt Creator 10.0.1
- Kit: Desktop Qt 6.5.0 GCC 64bit
@NightShadeI said in Detecting IF a key is being pressed:
- Hold 'W' key (for example), observe keyPressedEvent is called
- Start dragging the window while still holding W key, with your mouse. By dragging the window I mean clicking and holding on the window title bar and moving the mouse
- Release the 'W' key while still dragging. Observe the keyReleaseEvent is not called while this occurs.
- You will therefore get into some state with the window focused, and the application thinks the 'W' is still pressed, but it is not
I couldn't reproduce that, key release event gets called while still moving the window or resizing it. I tried different combinations of pressing, releasing, dragging, resizing, all resulted the same.
@NightShadeI said in Detecting IF a key is being pressed:
After you stop dragging the window, if you press the same key again, it won't register as a key press, but it will register as a key release once you raise the key
@NightShadeI said in Detecting IF a key is being pressed:
if you try to move or resize the widget while pressing any keyboard button , the event will never be called
Wouldn't that be because your MRE only detects key release events.
From what I understand, I made my own event filter, here it is:
bool eventFilter(QObject* aObject, QEvent* aEvent) final { //detect key release //the current time is just my way of preventing same output of debug messages if (aEvent->type() == QEvent::KeyRelease) { if (!static_cast<QKeyEvent*>(aEvent)->isAutoRepeat()) { qDebug() << "Key Released" << QTime::currentTime(); } } //detect key press if (aEvent->type() == QEvent::KeyPress) { if (!static_cast<QKeyEvent*>(aEvent)->isAutoRepeat()) { qDebug() << "Key Pressed" << QTime::currentTime() << "\n"; } } return QObject::eventFilter(aObject, aEvent); }
I tried multiple ways of pressing and releasing again, and everything seems to be normal.
I am no expert, just thought I'd share this in case it might be of some help. You can also correct me if I got it wrong (I feel like I'm missing something), to clarify it for others.
Good luck!
-
Hi,
First, here's my exact environment:
- System :
- Operating System: KDE neon 5.27
- KDE Plasma Version: 5.27.5
- KDE Frameworks Version: 5.106.0
- Qt Version: 5.15.9
- Kernel Version: 5.19.0-41-generic (64-bit)
- Graphics Platform: X11
- Developing tools :
- Qt 6.5.0
- CMake 3.22.1
- Qt Creator 10.0.1
- Kit: Desktop Qt 6.5.0 GCC 64bit
@NightShadeI said in Detecting IF a key is being pressed:
- Hold 'W' key (for example), observe keyPressedEvent is called
- Start dragging the window while still holding W key, with your mouse. By dragging the window I mean clicking and holding on the window title bar and moving the mouse
- Release the 'W' key while still dragging. Observe the keyReleaseEvent is not called while this occurs.
- You will therefore get into some state with the window focused, and the application thinks the 'W' is still pressed, but it is not
I couldn't reproduce that, key release event gets called while still moving the window or resizing it. I tried different combinations of pressing, releasing, dragging, resizing, all resulted the same.
@NightShadeI said in Detecting IF a key is being pressed:
After you stop dragging the window, if you press the same key again, it won't register as a key press, but it will register as a key release once you raise the key
@NightShadeI said in Detecting IF a key is being pressed:
if you try to move or resize the widget while pressing any keyboard button , the event will never be called
Wouldn't that be because your MRE only detects key release events.
From what I understand, I made my own event filter, here it is:
bool eventFilter(QObject* aObject, QEvent* aEvent) final { //detect key release //the current time is just my way of preventing same output of debug messages if (aEvent->type() == QEvent::KeyRelease) { if (!static_cast<QKeyEvent*>(aEvent)->isAutoRepeat()) { qDebug() << "Key Released" << QTime::currentTime(); } } //detect key press if (aEvent->type() == QEvent::KeyPress) { if (!static_cast<QKeyEvent*>(aEvent)->isAutoRepeat()) { qDebug() << "Key Pressed" << QTime::currentTime() << "\n"; } } return QObject::eventFilter(aObject, aEvent); }
I tried multiple ways of pressing and releasing again, and everything seems to be normal.
I am no expert, just thought I'd share this in case it might be of some help. You can also correct me if I got it wrong (I feel like I'm missing something), to clarify it for others.
Good luck!
@Abderrahmene_Rayene
Very much appreicate the attempt to reproduce! I don't have some sort of linux OS I can work with , so maybe it is only specific to OSX and Windows? Either way appreciate you helping with the investigation on this issue :)
I believe you have understood perfectly
Wouldn't that be because your MRE only detects key release events.
Just to clarify, I use keyPress and keyRelease in my actual code, just used keyRelease only in MRE to give the shorest possible code , as to try not to detour people , since both highlight the issue
- System :
-
@Abderrahmene_Rayene
Very much appreicate the attempt to reproduce! I don't have some sort of linux OS I can work with , so maybe it is only specific to OSX and Windows? Either way appreciate you helping with the investigation on this issue :)
I believe you have understood perfectly
Wouldn't that be because your MRE only detects key release events.
Just to clarify, I use keyPress and keyRelease in my actual code, just used keyRelease only in MRE to give the shorest possible code , as to try not to detour people , since both highlight the issue
@NightShadeI said in Detecting IF a key is being pressed:
just used keyRelease only in MRE to give the shorest possible code , as to try not to detour people , since both highlight the issue
I think you should include the key press event as well in your MRE, I thought you missed it.
And you're welcome, thanks for providing details, I always appreciate an MRE.
-
Can one of QT members please clarify if I should be raising a bug for this? Getting pretty desperate and it's still not solved
Or is there anything else I should provide?
-
Can one of QT members please clarify if I should be raising a bug for this? Getting pretty desperate and it's still not solved
Or is there anything else I should provide?
I wouldn't file a bug report unless you are sure they will be able to reproduce the behaviour. That's the key to having it addressed.
-
I wouldn't file a bug report unless you are sure they will be able to reproduce the behaviour. That's the key to having it addressed.
Is there anything else you think I should provide for this? I think I really just need someone else to try this on windows or OSX now, I've given steps and code
-
@JonB , @SGaist , @Christian-Ehrlicher , @Chris-Kawa or @jsulm
Are any of you able to provide any light on this issue? Thanks :)
-
@JonB , @SGaist , @Christian-Ehrlicher , @Chris-Kawa or @jsulm
Are any of you able to provide any light on this issue? Thanks :)
@NightShadeI Well, welcome to the world of
paininput handling :) This is not a Qt bug and you're not missing any events. You're just not getting any. There is no state that gets lost in Qt because it holds no state (apart from modifiers). It directly translates the OS messages to you e.g. on Windows theWM_[whatever]
native messages. You can install the native event filter on the app object and monitor all the raw messages OS sends to your windows if you want to see for yourself. It's the same if you use MFC or low level WinAPI. They all just work with what the system sends.A drag is denoted by
QEvent::NonClientAreaMouseButtonPress
andQEvent::NonClientAreaMouseButtonRelease
. I don't know about other OSes and I suspect this could vary on different window managers, but on Windows a window that is being dragged does not receive key press or release events. This means that:If you press before and release after a drag you will get both events.
If you press before and release during drag you will only get the press event.
If you press during and release after you will only get release.
If you press during, hold long enough to get autorepeat after drag and then release then it gets weird, because the first autorepeat after drag is converted to normal press, so you get normal press, then auto-repeat release without matching press, then a bunch of auto-repeat presses/releases pairs and a finishing normal release.I'd say it's a very niche corner case that may vary across platforms and there's no point in obsessing over it. The easiest way to handle this not to end up in an inconsistent state is to clear your key map when you get a non-client mouse press. You won't get any key presses until the drag is over and at worst you'll get an unmatched release after, but that's fine, since the map is cleared anyway.
If you want to loose some hair and actually handle this (I would advise against) you'd need to use the platform specific low level input APIs e.g. Direct Input or a keyboard hook with the
SetWindowsHookEx
API on Windows. I don't know about other OSes, but I suspect they have something similar. -
@NightShadeI Well, welcome to the world of
paininput handling :) This is not a Qt bug and you're not missing any events. You're just not getting any. There is no state that gets lost in Qt because it holds no state (apart from modifiers). It directly translates the OS messages to you e.g. on Windows theWM_[whatever]
native messages. You can install the native event filter on the app object and monitor all the raw messages OS sends to your windows if you want to see for yourself. It's the same if you use MFC or low level WinAPI. They all just work with what the system sends.A drag is denoted by
QEvent::NonClientAreaMouseButtonPress
andQEvent::NonClientAreaMouseButtonRelease
. I don't know about other OSes and I suspect this could vary on different window managers, but on Windows a window that is being dragged does not receive key press or release events. This means that:If you press before and release after a drag you will get both events.
If you press before and release during drag you will only get the press event.
If you press during and release after you will only get release.
If you press during, hold long enough to get autorepeat after drag and then release then it gets weird, because the first autorepeat after drag is converted to normal press, so you get normal press, then auto-repeat release without matching press, then a bunch of auto-repeat presses/releases pairs and a finishing normal release.I'd say it's a very niche corner case that may vary across platforms and there's no point in obsessing over it. The easiest way to handle this not to end up in an inconsistent state is to clear your key map when you get a non-client mouse press. You won't get any key presses until the drag is over and at worst you'll get an unmatched release after, but that's fine, since the map is cleared anyway.
If you want to loose some hair and actually handle this (I would advise against) you'd need to use the platform specific low level input APIs e.g. Direct Input or a keyboard hook with the
SetWindowsHookEx
API on Windows. I don't know about other OSes, but I suspect they have something similar.Thanks for the reply, I can't thank you for it enough. This is super useful and I believe I'll do the former of your approaches, sounds like a lot less of a headache :)
I'll test tonight and mark as solved if all is good! Hope this serves to help many others in future
-
@NightShadeI Well, welcome to the world of
paininput handling :) This is not a Qt bug and you're not missing any events. You're just not getting any. There is no state that gets lost in Qt because it holds no state (apart from modifiers). It directly translates the OS messages to you e.g. on Windows theWM_[whatever]
native messages. You can install the native event filter on the app object and monitor all the raw messages OS sends to your windows if you want to see for yourself. It's the same if you use MFC or low level WinAPI. They all just work with what the system sends.A drag is denoted by
QEvent::NonClientAreaMouseButtonPress
andQEvent::NonClientAreaMouseButtonRelease
. I don't know about other OSes and I suspect this could vary on different window managers, but on Windows a window that is being dragged does not receive key press or release events. This means that:If you press before and release after a drag you will get both events.
If you press before and release during drag you will only get the press event.
If you press during and release after you will only get release.
If you press during, hold long enough to get autorepeat after drag and then release then it gets weird, because the first autorepeat after drag is converted to normal press, so you get normal press, then auto-repeat release without matching press, then a bunch of auto-repeat presses/releases pairs and a finishing normal release.I'd say it's a very niche corner case that may vary across platforms and there's no point in obsessing over it. The easiest way to handle this not to end up in an inconsistent state is to clear your key map when you get a non-client mouse press. You won't get any key presses until the drag is over and at worst you'll get an unmatched release after, but that's fine, since the map is cleared anyway.
If you want to loose some hair and actually handle this (I would advise against) you'd need to use the platform specific low level input APIs e.g. Direct Input or a keyboard hook with the
SetWindowsHookEx
API on Windows. I don't know about other OSes, but I suspect they have something similar.Works perfectly on both Windows and OSX!
I'll leave a footnote for anyone in future, that it seems that on OSX you have to actually install a event filter into the QApplication to handle the event, the QWidget does not seem to receive this event, where as it seems to on Windows.
Thanks Chris again for the response, will mark as solved
-