Bug: the `released` signal will not be properly emitted when pressing multiple hotkeys at the same time on windows
FredBill1 opened this issue · 0 comments
Steps to reproduce
I'm using Qt6.4.3 on windows 11. Here is a minimum demo code for reproducing the bug:
#include <QApplication>
#include <QDebug>
#include <QHotkey>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QHotkey key_a(QKeySequence(Qt::Key_A), true);
QHotkey key_b(QKeySequence(Qt::Key_B), true);
QObject::connect(&key_a, &QHotkey::activated, [&]() { qDebug() << "A pressed"; });
QObject::connect(&key_b, &QHotkey::activated, [&]() { qDebug() << "B pressed"; });
QObject::connect(&key_a, &QHotkey::released, [&]() { qDebug() << "A released"; });
QObject::connect(&key_b, &QHotkey::released, [&]() { qDebug() << "B released"; });
return app.exec();
}
Run the code in a debugger, and press the keys as the following sequence: press A-> press B -> release A -> release B
.
The expected console output would be:
A pressed
B pressed
A released
B released
But the actual output is:
A pressed
B pressed
B released
Where A released
is missing. Further more, when pressing multiple hotkeys at the same time, only the last hotkey would emit the released
signal.
Cause of the bug
The QHotkeyPrivateWin
class only polls the release of the last hotkey being pressed, and the previous hotkeys are replaced by the new pressed hotkey. The link to the related code is here.
bool QHotkeyPrivateWin::nativeEventFilter(const QByteArray &eventType, void *message, _NATIVE_EVENT_RESULT *result)
{
Q_UNUSED(eventType)
Q_UNUSED(result)
MSG* msg = static_cast<MSG*>(message);
if(msg->message == WM_HOTKEY) {
QHotkey::NativeShortcut shortcut = {HIWORD(msg->lParam), LOWORD(msg->lParam)};
this->activateShortcut(shortcut);
this->polledShortcut = shortcut; //! previous pressed hotkeys being replaced
this->pollTimer.start();
}
return false;
}
void QHotkeyPrivateWin::pollForHotkeyRelease()
{
//! only polls the last pressed hotkey
bool pressed = (GetAsyncKeyState(this->polledShortcut.key) & (1 << 15)) != 0;
if(!pressed) {
this->pollTimer.stop();
this->releaseShortcut(this->polledShortcut);
}
}
Possible solution
Use a container like QList<QHotkey::NativeShortcut>
to maintain all the keys being pressed but not yet released, and use std::remove_if
to emit the released
signal and remove the corresponding NativeShortcut
from the container.