From d274916c20f6f11f57fa1f5f1c0863458c9847c6 Mon Sep 17 00:00:00 2001 From: Janek Bevendorff Date: Mon, 21 Oct 2019 10:49:09 +0200 Subject: [PATCH] Implement Caps Lock warning on Linux (X11) and Windows --- CMakeLists.txt | 2 +- src/CMakeLists.txt | 2 +- src/gui/DatabaseOpenWidget.cpp | 6 ++++ src/gui/DatabaseOpenWidget.ui | 32 +++++++++++++----- src/gui/PasswordEdit.cpp | 60 ++++++++++++++++++++++++++++++++++ src/gui/PasswordEdit.h | 10 ++++++ 6 files changed, 101 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2264609877..0196b44126 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -369,7 +369,7 @@ include(CLangFormat) set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools) if(UNIX AND NOT APPLE) - find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED) + find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus X11Extras REQUIRED) elseif(APPLE) find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED HINTS /usr/local/opt/qt/lib/cmake /usr/local/Cellar/qt/*/lib/cmake ENV PATH) find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/opt/qt/lib/cmake /usr/local/Cellar/qt/*/lib/cmake ENV PATH) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 77acf290ea..01afd96785 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -329,7 +329,7 @@ if(HAIKU) target_link_libraries(keepassx_core network) endif() if(UNIX AND NOT APPLE) - target_link_libraries(keepassx_core Qt5::DBus) + target_link_libraries(keepassx_core Qt5::DBus Qt5::X11Extras X11) endif() if(MINGW) target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 9ea2b1ac61..3e2df60d0d 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -79,6 +79,12 @@ DatabaseOpenWidget::DatabaseOpenWidget(QWidget* parent) m_ui->yubikeyProgress->setSizePolicy(sp); connect(m_ui->buttonRedetectYubikey, SIGNAL(clicked()), SLOT(pollYubikey())); + + m_ui->capsLockWarningLabel->setVisible(false); + connect(m_ui->editPassword, &PasswordEdit::capslockToggled, [&](bool state) { + m_ui->capsLockWarningLabel->setVisible(state); + }); + #else m_ui->buttonRedetectYubikey->setVisible(false); m_ui->comboChallengeResponse->setVisible(false); diff --git a/src/gui/DatabaseOpenWidget.ui b/src/gui/DatabaseOpenWidget.ui index a138aab0f0..1e018f2904 100644 --- a/src/gui/DatabaseOpenWidget.ui +++ b/src/gui/DatabaseOpenWidget.ui @@ -7,7 +7,7 @@ 0 0 592 - 462 + 522 @@ -155,14 +155,28 @@ - - - Enter Password: - - - editPassword - - + + + + + Enter Password: + + + editPassword + + + + + + + QLabel { color: rgb(255, 125, 125); } + + + Warning: Caps Lock enabled + + + + diff --git a/src/gui/PasswordEdit.cpp b/src/gui/PasswordEdit.cpp index 37b82ad8b6..590d62b9ce 100644 --- a/src/gui/PasswordEdit.cpp +++ b/src/gui/PasswordEdit.cpp @@ -22,12 +22,30 @@ #include "core/FilePath.h" #include "gui/Font.h" +#include +#include +#include + +#if defined(Q_OS_WIN) +#include +#elif defined(Q_OS_MACOS) +// TODO +#elif defined(Q_OS_UNIX) +#include +// namespace required to avoid name clashes with declarations in XKBlib.h +namespace X11 +{ +#include +} +#endif + const QColor PasswordEdit::CorrectSoFarColor = QColor(255, 205, 15); const QColor PasswordEdit::ErrorColor = QColor(255, 125, 125); PasswordEdit::PasswordEdit(QWidget* parent) : QLineEdit(parent) , m_basePasswordEdit(nullptr) + , m_capslockPollTimer(new QTimer(this)) { const QIcon errorIcon = filePath()->icon("status", "dialog-error"); m_errorAction = addAction(errorIcon, QLineEdit::TrailingPosition); @@ -46,6 +64,8 @@ PasswordEdit::PasswordEdit(QWidget* parent) QFont passwordFont = Font::fixedFont(); passwordFont.setLetterSpacing(QFont::PercentageSpacing, 110); setFont(passwordFont); + + connect(m_capslockPollTimer, SIGNAL(timeout()), SLOT(checkCapslockState())); } void PasswordEdit::enableVerifyMode(PasswordEdit* basePasswordEdit) @@ -119,3 +139,43 @@ void PasswordEdit::autocompletePassword(const QString& password) setText(password); } } + +void PasswordEdit::hideEvent(QHideEvent* event) +{ + QWidget::hideEvent(event); + m_capslockPollTimer->stop(); +} + +void PasswordEdit::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + if (!m_basePasswordEdit) { + // poll caps lock state only for primary password edits + m_capslockPollTimer->start(50); + } +} + +void PasswordEdit::checkCapslockState() +{ + bool newCapslockState = m_capslockState; + +#if defined(Q_OS_WIN) + newCapslockState = (GetKeyState(VK_CAPITAL) == 1); +#elif defined(Q_OS_MACOS) + // TODO +#elif defined(Q_OS_UNIX) + if (QX11Info::isPlatformX11() && QX11Info::display()) { + unsigned state = 0; + // reinterpret cast needed, since we namespaced the XKBlib.h include + if (X11::XkbGetIndicatorState( + reinterpret_cast(QX11Info::display()), XkbUseCoreKbd, &state) == Success) { + newCapslockState = ((state & 1u) == 1); + } + } +#endif + + if (newCapslockState != m_capslockState) { + m_capslockState = newCapslockState; + emit capslockToggled(m_capslockState); + } +} diff --git a/src/gui/PasswordEdit.h b/src/gui/PasswordEdit.h index b6e74ed00d..2368472208 100644 --- a/src/gui/PasswordEdit.h +++ b/src/gui/PasswordEdit.h @@ -23,6 +23,8 @@ #include #include +class QTimer; + class PasswordEdit : public QLineEdit { Q_OBJECT @@ -38,19 +40,27 @@ class PasswordEdit : public QLineEdit public slots: void setShowPassword(bool show); +protected: + void showEvent(QShowEvent* event) override; + void hideEvent(QHideEvent* event) override; + signals: void showPasswordChanged(bool show); + void capslockToggled(bool capslockOn); private slots: void updateStylesheet(); void autocompletePassword(const QString& password); + void checkCapslockState(); private: bool passwordsEqual() const; + bool m_capslockState = false; QPointer m_errorAction; QPointer m_correctAction; QPointer m_basePasswordEdit; + QPointer m_capslockPollTimer; }; #endif // KEEPASSX_PASSWORDEDIT_H