How to build WM_KEYDOWN|UP LPARAM from a QKeyEvent?
-
I'm trying to replicate
QKeyEventssent on aQWidgetto another non Qt Window, for this I'm using the WINAPIPostMessagetogetherWM_KEYDOWN / WM_KEYUPmessagesBased on this stack answer
and Microsoft documentation of Keystroke Message FlagsI wrote this small compilable example:
// mainwindow.h QT_BEGIN_NAMESPACE namespace Ui { class MainWindowClass; }; QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); bool eventFilter(QObject* obj, QEvent* event); protected: virtual bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override; private: Ui::MainWindowClass *ui; }; // mainwindow.cpp #include "MainWindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindowClass()) { ui->setupUi(this); QTextEdit* textEdit = new QTextEdit(this); textEdit->installEventFilter(this); textEdit->setGeometry(50, 50, 200, 50); } bool MainWindow::eventFilter(QObject* obj, QEvent* event) { switch (event->type()) { case QEvent::KeyPress: case QEvent::KeyRelease: { QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); WPARAM wParam = keyEvent->nativeVirtualKey(); LPARAM lParam = 0; lParam |= (keyEvent->type() == QEvent::KeyPress) ? (0) : (1 << 31); // Transition state lParam |= (keyEvent->isAutoRepeat()) ? (1 << 30) : 0; // Previous key state lParam |= (keyEvent->modifiers() & Qt::ShiftModifier) ? (1 << 24) : 0; lParam |= (keyEvent->modifiers() & Qt::ControlModifier) ? (1 << 25) : 0; lParam |= (keyEvent->modifiers() & Qt::AltModifier) ? (1 << 26) : 0; lParam |= (keyEvent->modifiers() & Qt::MetaModifier) ? (1 << 27) : 0; bool isExtendedKey = (keyEvent->nativeScanCode() & 0x0100) != 0; // Extended key lParam |= (isExtendedKey ? (1 << 24) : 0); lParam |= (MapVirtualKey(wParam, MAPVK_VK_TO_VSC) << 16); // Scan code lParam |= (keyEvent->type() == QEvent::KeyPress) ? (1) : 0; // Repeat count qDebug() << (keyEvent->type() == QEvent::KeyPress ? "[[KEYDOWN]]" : "[[KEYUP]]") << "\nwParam" << wParam << "\nlParam" << lParam << "\n"; //PostMessage(hwnd, keyEvent->type() == QEvent::KeyPress ? WM_KEYDOWN : WM_KEYUP, wParam, lParam); } } return false; } bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result) { MSG* msg = static_cast<MSG*>(message); switch (msg->message) { case WM_KEYDOWN: case WM_KEYUP: { qDebug() << (msg->message == WM_KEYDOWN ? "KEYDOWN" : "KEYUP") << "\nwParam:" << msg->wParam << "\nlParam:" << msg->lParam << "\n";; break; } default: break; } return QWidget::nativeEvent(eventType, message, result); }The
lParamfrom thenativeEventis different from the ones i'm building inside theeventFilter, trying to figure out what I'm calculating wrong. -
@Chris-Kawa Thank you, now the values are equal, i was getting the same issue for the
QWheelEventthe value when delta.y < 0 was being different from the wParam ofWM_MOUSEWHEELcase QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event); // https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel int stateFlags = 0; if (wheelEvent->buttons() & Qt::LeftButton) stateFlags |= MK_LBUTTON; if (wheelEvent->buttons() & Qt::RightButton) stateFlags |= MK_RBUTTON; if (wheelEvent->modifiers() & Qt::ShiftModifier) stateFlags |= MK_SHIFT; if (wheelEvent->modifiers() & Qt::ControlModifier) stateFlags |= MK_CONTROL; qDebug() << "modifier:" << wheelEvent->modifiers(); qDebug() << "stateFlags:" << stateFlags; WPARAM wParam = 0; if (wheelEvent->angleDelta().y() > 0) wParam = (120 << 16) | (stateFlags & 0xffff); else if (wheelEvent->angleDelta().y() < 0) wParam = (-120ll << 16) | (stateFlags & 0xffff);then i changed
wParam = (-120 << 16) | (stateFlags & 0xffff);
to
wParam = (-120ll << 16) | (stateFlags & 0xffff);
now the wParam match.I didn't understand this
llthing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?A side question, why Qt use a different value for the key modifiers
Qt::ControlModifier, etc, than Microsoft? it took a lot of time to figure out it.
Qt::ControlModifier0x04000000while from MicrosoftMK_CONTROLis0x0008@Ylvy said:
I didn't understand this ll thing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?
LPARAMis a typedef for__int64, which is Microsoft's extension type name for 64bit signed integers (same asint64_tin modern C++), so when dealing with constants for it you should be using signed 64bit integers, for example1LL. As @JonB said it stands forlong long int, which is the old way of standard C++ to mean "at least 64 bit" (it is always 64 bit on 64bit Windows).WPARAMis a typedef forunsigned __int64, so when dealing with it you should be using unsigned 64bit integers, for example1uLL, which stands forunsigned long long int.The type of
(1<<31)expression isint, since all operands are int and that is a 32bit signed integer on Windows. When using it in expression likelParam | (1 << 31)it gets upcasted to 64bit type, because the left operand is a signed 64bit value.(1<<31)is a negative 32bit signed value, so when upcasted to 64bit you get11111...at the extended part of the 64bit value (C++ uses Two's complement binary representation), which caused your problem. A type of(1LL << 31)expression islong long, since the left operand is along longconstant. It already is a 64bit value with00000....on the first 32 bits, so there's no upcasing needed when you | it with lParam.Btw. keep in mind that what I said is about 64bit Windows. If you target 32bit Windows too then LPARAM and WPARAM are typedefs for 32bit types
longandunsigned int, so use 32bit constants there.A side question, why Qt use a different value for the key modifiers Qt::ControlModifier, etc, than Microsoft?
Why should it be the same as Microsoft? Qt is a cross-platform framework with roots in Linux. I don't see any reason why it should use the same values for private implementation as any particular vendor, not just Microsoft.
-
I see a couple problems:
-
Repeat count should always be 1 for key releases and >=1 for key presses. It's more than 1 when app is slow to respond and Windows compresses auto repeat messages into single message. For purposes of synthesizing this message you should always put 1 there and Windows will compress them if it feels like it.
-
Previous key state should be 1 for autorepeat and key release.
-
ALT key should be encoded in the (1 << 29) bit (Context Code).
-
You've got two flags in bit (1<<24). It should only hold the extended key flag.
-
I don't see where you got these bit numbers 24-27 from, but that's not the right way to handle these modifiers. The state of the SHIFT, ALT and CTRL keys is a bit tricky, as they can get incorporated into the scan code. Also there's left and right keys for those, which Qt unfortunately doesn't differentiate. You'll have to work them out from the scan code and extended key flag.
-
-
I see a couple problems:
-
Repeat count should always be 1 for key releases and >=1 for key presses. It's more than 1 when app is slow to respond and Windows compresses auto repeat messages into single message. For purposes of synthesizing this message you should always put 1 there and Windows will compress them if it feels like it.
-
Previous key state should be 1 for autorepeat and key release.
-
ALT key should be encoded in the (1 << 29) bit (Context Code).
-
You've got two flags in bit (1<<24). It should only hold the extended key flag.
-
I don't see where you got these bit numbers 24-27 from, but that's not the right way to handle these modifiers. The state of the SHIFT, ALT and CTRL keys is a bit tricky, as they can get incorporated into the scan code. Also there's left and right keys for those, which Qt unfortunately doesn't differentiate. You'll have to work them out from the scan code and extended key flag.
I fixed the things you said, im testing hitting "a" in the
QTextEdit, this is the values i get in qDebugKEYDOWN wParam 65 lParam 1966081 scanCode: 30 [[KEYDOWN]] wParam 65 lParam 1966081 scanCode 30 KEYUP wParam 65 lParam 3223191553 scanCode: 30 [[KEYUP]] wParam 65 lParam -1071775743 scanCode 30QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); WPARAM wParam = keyEvent->nativeVirtualKey(); bool keyRelease = keyEvent->type() == QEvent::KeyRelease; LPARAM lParam = 0; lParam |= (keyRelease) ? (1 << 31): (0); // Transition state lParam |= (keyEvent->isAutoRepeat() || keyRelease) ? (1 << 30) : 0; // Previous key state lParam |= (keyEvent->modifiers() & Qt::AltModifier) ? (1 << 29) : 0; // Context code bool isExtendedKey = (keyEvent->nativeScanCode() & 0x0100) != 0; // Extended key lParam |= (isExtendedKey ? (1 << 24) : 0); lParam |= (keyEvent->nativeScanCode() << 16); // Scan code lParam |= 1; // Repeat count // Just for debugging: WORD keyFlags = HIWORD(lParam); WORD scanCode = LOBYTE(keyFlags); // scan code qDebug() << (keyRelease ? "[[KEYUP]]" : "[[KEYDOWN]]") << "\nwParam" << wParam << "lParam" << lParam << "\nscanCode" << scanCode << "\n";Apparently now, its different only when its the keyRelease.
-
-
I fixed the things you said, im testing hitting "a" in the
QTextEdit, this is the values i get in qDebugKEYDOWN wParam 65 lParam 1966081 scanCode: 30 [[KEYDOWN]] wParam 65 lParam 1966081 scanCode 30 KEYUP wParam 65 lParam 3223191553 scanCode: 30 [[KEYUP]] wParam 65 lParam -1071775743 scanCode 30QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); WPARAM wParam = keyEvent->nativeVirtualKey(); bool keyRelease = keyEvent->type() == QEvent::KeyRelease; LPARAM lParam = 0; lParam |= (keyRelease) ? (1 << 31): (0); // Transition state lParam |= (keyEvent->isAutoRepeat() || keyRelease) ? (1 << 30) : 0; // Previous key state lParam |= (keyEvent->modifiers() & Qt::AltModifier) ? (1 << 29) : 0; // Context code bool isExtendedKey = (keyEvent->nativeScanCode() & 0x0100) != 0; // Extended key lParam |= (isExtendedKey ? (1 << 24) : 0); lParam |= (keyEvent->nativeScanCode() << 16); // Scan code lParam |= 1; // Repeat count // Just for debugging: WORD keyFlags = HIWORD(lParam); WORD scanCode = LOBYTE(keyFlags); // scan code qDebug() << (keyRelease ? "[[KEYUP]]" : "[[KEYDOWN]]") << "\nwParam" << wParam << "lParam" << lParam << "\nscanCode" << scanCode << "\n";Apparently now, its different only when its the keyRelease.
@Ylvy Oh, right. It's just a silly casting issue. It's because LPARAM is int64 and (1 << 31) is int32 value of -1, so it gets sign extended to 64 bits when converting to LPARAM for the | operator. Simply use the 64 bit type for 1, so
lParam |= (keyRelease) ? (1ll << 31): (0);that's 1 followed by two lowercase L.
-
@Ylvy Oh, right. It's just a silly casting issue. It's because LPARAM is int64 and (1 << 31) is int32 value of -1, so it gets sign extended to 64 bits when converting to LPARAM for the | operator. Simply use the 64 bit type for 1, so
lParam |= (keyRelease) ? (1ll << 31): (0);that's 1 followed by two lowercase L.
@Chris-Kawa Thank you, now the values are equal, i was getting the same issue for the
QWheelEventthe value when delta.y < 0 was being different from the wParam ofWM_MOUSEWHEELcase QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event); // https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel int stateFlags = 0; if (wheelEvent->buttons() & Qt::LeftButton) stateFlags |= MK_LBUTTON; if (wheelEvent->buttons() & Qt::RightButton) stateFlags |= MK_RBUTTON; if (wheelEvent->modifiers() & Qt::ShiftModifier) stateFlags |= MK_SHIFT; if (wheelEvent->modifiers() & Qt::ControlModifier) stateFlags |= MK_CONTROL; qDebug() << "modifier:" << wheelEvent->modifiers(); qDebug() << "stateFlags:" << stateFlags; WPARAM wParam = 0; if (wheelEvent->angleDelta().y() > 0) wParam = (120 << 16) | (stateFlags & 0xffff); else if (wheelEvent->angleDelta().y() < 0) wParam = (-120ll << 16) | (stateFlags & 0xffff);then i changed
wParam = (-120 << 16) | (stateFlags & 0xffff);
to
wParam = (-120ll << 16) | (stateFlags & 0xffff);
now the wParam match.I didn't understand this
llthing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?A side question, why Qt use a different value for the key modifiers
Qt::ControlModifier, etc, than Microsoft? it took a lot of time to figure out it.
Qt::ControlModifier0x04000000while from MicrosoftMK_CONTROLis0x0008 -
@Chris-Kawa Thank you, now the values are equal, i was getting the same issue for the
QWheelEventthe value when delta.y < 0 was being different from the wParam ofWM_MOUSEWHEELcase QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event); // https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel int stateFlags = 0; if (wheelEvent->buttons() & Qt::LeftButton) stateFlags |= MK_LBUTTON; if (wheelEvent->buttons() & Qt::RightButton) stateFlags |= MK_RBUTTON; if (wheelEvent->modifiers() & Qt::ShiftModifier) stateFlags |= MK_SHIFT; if (wheelEvent->modifiers() & Qt::ControlModifier) stateFlags |= MK_CONTROL; qDebug() << "modifier:" << wheelEvent->modifiers(); qDebug() << "stateFlags:" << stateFlags; WPARAM wParam = 0; if (wheelEvent->angleDelta().y() > 0) wParam = (120 << 16) | (stateFlags & 0xffff); else if (wheelEvent->angleDelta().y() < 0) wParam = (-120ll << 16) | (stateFlags & 0xffff);then i changed
wParam = (-120 << 16) | (stateFlags & 0xffff);
to
wParam = (-120ll << 16) | (stateFlags & 0xffff);
now the wParam match.I didn't understand this
llthing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?A side question, why Qt use a different value for the key modifiers
Qt::ControlModifier, etc, than Microsoft? it took a lot of time to figure out it.
Qt::ControlModifier0x04000000while from MicrosoftMK_CONTROLis0x0008@Ylvy said in How to build WM_KEYDOWN|UP LPARAM from a QKeyEvent?:
I didn't understand this ll thing
1llis just a way of writing a constant in C++. The constant's value is1. Thellsuffix stands forlong-long, which in practice means make it a 64-bit number. (You can also write it with uppercase letter, as1LL, which personally I prefer.) You could also write it as(long long int) 1or(int64) 1. So the value written in hex is0x0000000000000001. @Chris-Kawa explained why a 64-bit rather than a standard 32-bitintis required here for working withLPARAM. -
@Chris-Kawa Thank you, now the values are equal, i was getting the same issue for the
QWheelEventthe value when delta.y < 0 was being different from the wParam ofWM_MOUSEWHEELcase QEvent::Wheel: { QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event); // https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel int stateFlags = 0; if (wheelEvent->buttons() & Qt::LeftButton) stateFlags |= MK_LBUTTON; if (wheelEvent->buttons() & Qt::RightButton) stateFlags |= MK_RBUTTON; if (wheelEvent->modifiers() & Qt::ShiftModifier) stateFlags |= MK_SHIFT; if (wheelEvent->modifiers() & Qt::ControlModifier) stateFlags |= MK_CONTROL; qDebug() << "modifier:" << wheelEvent->modifiers(); qDebug() << "stateFlags:" << stateFlags; WPARAM wParam = 0; if (wheelEvent->angleDelta().y() > 0) wParam = (120 << 16) | (stateFlags & 0xffff); else if (wheelEvent->angleDelta().y() < 0) wParam = (-120ll << 16) | (stateFlags & 0xffff);then i changed
wParam = (-120 << 16) | (stateFlags & 0xffff);
to
wParam = (-120ll << 16) | (stateFlags & 0xffff);
now the wParam match.I didn't understand this
llthing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?A side question, why Qt use a different value for the key modifiers
Qt::ControlModifier, etc, than Microsoft? it took a lot of time to figure out it.
Qt::ControlModifier0x04000000while from MicrosoftMK_CONTROLis0x0008@Ylvy said:
I didn't understand this ll thing, based on these two examples i gave (wParam of wheel and lParam of KeyEvent) is it still possible to it construct wrong messages at sometime or is it correct now?
LPARAMis a typedef for__int64, which is Microsoft's extension type name for 64bit signed integers (same asint64_tin modern C++), so when dealing with constants for it you should be using signed 64bit integers, for example1LL. As @JonB said it stands forlong long int, which is the old way of standard C++ to mean "at least 64 bit" (it is always 64 bit on 64bit Windows).WPARAMis a typedef forunsigned __int64, so when dealing with it you should be using unsigned 64bit integers, for example1uLL, which stands forunsigned long long int.The type of
(1<<31)expression isint, since all operands are int and that is a 32bit signed integer on Windows. When using it in expression likelParam | (1 << 31)it gets upcasted to 64bit type, because the left operand is a signed 64bit value.(1<<31)is a negative 32bit signed value, so when upcasted to 64bit you get11111...at the extended part of the 64bit value (C++ uses Two's complement binary representation), which caused your problem. A type of(1LL << 31)expression islong long, since the left operand is along longconstant. It already is a 64bit value with00000....on the first 32 bits, so there's no upcasing needed when you | it with lParam.Btw. keep in mind that what I said is about 64bit Windows. If you target 32bit Windows too then LPARAM and WPARAM are typedefs for 32bit types
longandunsigned int, so use 32bit constants there.A side question, why Qt use a different value for the key modifiers Qt::ControlModifier, etc, than Microsoft?
Why should it be the same as Microsoft? Qt is a cross-platform framework with roots in Linux. I don't see any reason why it should use the same values for private implementation as any particular vendor, not just Microsoft.
-
Y Ylvy has marked this topic as solved on