Implementation Overview
This article demonstrates three approaches for detecting Caps Lock key state changes in Qt applications, with focus on providing real-time visual feedback to users during password entry.
Caps Lock Detection Method
The fundamental technique for determining Caps Lock state uses the Windows API:
bool KeyboardMonitor::checkCapsLockState()
{
return LOBYTE(GetKeyState(VK_CAPITAL)) != 0;
}
This function returns true when Caps Lock is active and false when inactive.
Approach 1: Widget Event Handling
Override keyboard event handlers in a custom QLineEdit widget:
class SecureInputField : public QLineEdit
{
protected:
void keyPressEvent(QKeyEvent* event) override
{
if (event->key() == Qt::Key_CapsLock) {
updateCapsIndicator();
}
QLineEdit::keyPressEvent(event);
}
void keyReleaseEvent(QKeyEvent* event) override
{
if (event->key() == Qt::Key_CapsLock) {
updateCapsIndicator();
}
QLineEdit::keyReleaseEvent(event);
}
private:
void updateCapsIndicator()
{
if (checkCapsLockState()) {
indicatorWidget->showWarning();
} else {
indicatorWidget->hideWarning();
}
}
};
Limitation: This only works when the widget has keyboard focus.
Approach 2: Application-Level Event Filtering
Implement native event filtering for application-wide key monitoring:
class GlobalKeyWatcher : public QAbstractNativeEventFilter
{
public:
bool nativeEventFilter(const QByteArray &eventType,
void* message, long* result) override
{
if (eventType == "windows_dispatcher_MSG") {
MSG* msg = static_cast<msg>(message);
if (msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
if (static_cast<int>(msg->wParam) == VK_CAPITAL) {
emit capsLockStateChanged();
return false;
}
}
}
return false;
}
};
</int></msg>
Limitation: Only captures events when the application is active.
Approach 3: System-Wide Keyboard Hook
Implement a low-level keyboard hook for system-wide monitoring:
class SystemKeyboardHook
{
public:
SystemKeyboardHook()
{
hookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
hookCallback,
GetModuleHandle(nullptr),
0);
}
~SystemKeyboardHook()
{
if (hookHandle) {
UnhookWindowsHookEx(hookHandle);
}
}
void setCallback(std::function<void> callback) {
userCallback = callback;
}
private:
static LRESULT CALLBACK hookCallback(int code, WPARAM wParam, LPARAM lParam)
{
if (code >= 0 && wParam == WM_KEYDOWN) {
KBDLLHOOKSTRUCT* keyData = reinterpret_cast<kbdllhookstruct>(lParam);
if (keyData->vkCode == VK_CAPITAL && userCallback) {
userCallback();
}
}
return CallNextHookEx(nullptr, code, wParam, lParam);
}
static inline HHOOK hookHandle = nullptr;
static inline std::function<void> userCallback;
};
</void></kbdllhookstruct></void>
Complete Implementation Example
Integrate the system hook with Qt widget:
class PasswordEntryField : public QLineEdit
{
public:
PasswordEntryField(QWidget* parent = nullptr) : QLineEdit(parent)
{
keyboardHook.setCallback([this]() { refreshCapsDisplay(); });
refreshCapsDisplay();
}
private:
void refreshCapsDisplay()
{
if (checkCapsLockState()) {
setStyleSheet("background-color: #fff0f0;");
setPlaceholderText("CAPS LOCK IS ACTIVE");
} else {
setStyleSheet("");
setPlaceholderText("Enter password...");
}
}
SystemKeyboardHook keyboardHook;
};
The system hook approach provides reliable Caps Lock monitoring regardless of application focus state.