Skip to content

Commit

Permalink
Implement Caps Lock warning
Browse files Browse the repository at this point in the history
  • Loading branch information
phoerious committed May 2, 2020
1 parent 5add012 commit 089fe91
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ 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 X11)
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if(MINGW)
target_link_libraries(keepassx_core Wtsapi32.lib Ws2_32.lib)
Expand Down
32 changes: 20 additions & 12 deletions src/core/Resources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,11 @@ QIcon Resources::trayIconUnlocked()
return useDarkIcon() ? icon("keepassxc-dark", false) : icon("keepassxc-unlocked", false);
}

QIcon Resources::icon(const QString& name, bool recolor)
QIcon Resources::icon(const QString& name, bool recolor, const QColor& overrideColor)
{
QIcon icon = m_iconCache.value(name);

if (!icon.isNull()) {
if (!icon.isNull() && !overrideColor.isValid()) {
return icon;
}

Expand All @@ -128,28 +128,36 @@ QIcon Resources::icon(const QString& name, bool recolor)
QImage img = icon.pixmap(128, 128).toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
icon = {};

QPalette palette = getMainWindow()->palette();
QPainter painter(&img);
painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);

painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Normal, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
if (!overrideColor.isValid()) {
QPalette palette = getMainWindow()->palette();
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Normal, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);

painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::ButtonText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Active);
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::ButtonText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Active);

painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::HighlightedText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected);
painter
.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Active, QPalette::HighlightedText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Selected);

painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Disabled, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Disabled);
painter.fillRect(0, 0, img.width(), img.height(), palette.color(QPalette::Disabled, QPalette::WindowText));
icon.addPixmap(QPixmap::fromImage(img), QIcon::Disabled);
} else {
painter.fillRect(0, 0, img.width(), img.height(), overrideColor);
icon.addPixmap(QPixmap::fromImage(img), QIcon::Normal);
}

#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
icon.setIsMask(true);
#endif
}

m_iconCache.insert(name, icon);
if (!overrideColor.isValid()) {
m_iconCache.insert(name, icon);
}

return icon;
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/Resources.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef KEEPASSX_RESOURCES_H
#define KEEPASSX_RESOURCES_H

#include <QColor>
#include <QHash>
#include <QIcon>
#include <QString>
Expand All @@ -33,7 +34,7 @@ class Resources
QIcon trayIcon();
QIcon trayIconLocked();
QIcon trayIconUnlocked();
QIcon icon(const QString& name, bool recolor = true);
QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid);
QIcon onOffIcon(const QString& name, bool recolor = true);

static Resources* instance();
Expand Down
52 changes: 47 additions & 5 deletions src/gui/PasswordEdit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,17 @@
#include "core/Resources.h"
#include "gui/Font.h"
#include "gui/PasswordGeneratorWidget.h"
#include "gui/osutils/OSUtils.h"
#include "gui/styles/StateColorPalette.h"

#include <QDialog>
#include <QTimer>
#include <QToolTip>
#include <QVBoxLayout>

namespace
{

} // namespace

PasswordEdit::PasswordEdit(QWidget* parent)
: QLineEdit(parent)
, m_capslockPollTimer(new QTimer(this))
{
const QIcon errorIcon = resources()->icon("dialog-error");
m_errorAction = addAction(errorIcon, QLineEdit::TrailingPosition);
Expand Down Expand Up @@ -70,12 +69,21 @@ PasswordEdit::PasswordEdit(QWidget* parent)
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
m_passwordGeneratorAction->setVisible(false);

m_capslockAction = new QAction(
resources()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
tr("Warning: Caps Lock enabled!"), nullptr);
addAction(m_capslockAction, QLineEdit::LeadingPosition);
m_capslockAction->setVisible(false);

connect(m_capslockPollTimer, SIGNAL(timeout()), SLOT(checkCapslockState()));
}

void PasswordEdit::setRepeatPartner(PasswordEdit* repeatEdit)
{
m_repeatPasswordEdit = repeatEdit;
m_repeatPasswordEdit->setParentPasswordEdit(this);
m_repeatPasswordEdit->m_capslockPollTimer->stop();

connect(this, SIGNAL(textChanged(QString)), m_repeatPasswordEdit, SLOT(autocompletePassword(QString)));
connect(this, SIGNAL(textChanged(QString)), m_repeatPasswordEdit, SLOT(updateRepeatStatus()));
Expand Down Expand Up @@ -165,3 +173,37 @@ 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);
m_capslockPollTimer->start(50);
}

void PasswordEdit::checkCapslockState()
{
if (m_parentPasswordEdit) {
return;
}

bool newCapslockState = osUtils->isCapslockEnabled();
if (newCapslockState != m_capslockState) {
m_capslockState = newCapslockState;
m_capslockAction->setVisible(newCapslockState);

// Force repaint to avoid rendering glitches of QLineEdit contents
repaint();

emit capslockToggled(m_capslockState);

if (newCapslockState) {
QToolTip::showText(mapToGlobal(rect().bottomLeft()), m_capslockAction->text());
}
}
}
14 changes: 12 additions & 2 deletions src/gui/PasswordEdit.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <QPointer>

class QDialog;
class QTimer;

class PasswordEdit : public QLineEdit
{
Expand All @@ -39,20 +40,29 @@ public slots:
void setShowPassword(bool show);
void updateRepeatStatus();

protected:
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;

signals:
void capslockToggled(bool capslockOn);

private slots:
void autocompletePassword(const QString& password);
void popupPasswordGenerator();
void setParentPasswordEdit(PasswordEdit* parent);
void checkCapslockState();

private:
QPointer<QAction> m_errorAction;
QPointer<QAction> m_correctAction;
QPointer<QAction> m_toggleVisibleAction;
QPointer<QAction> m_passwordGeneratorAction;
QPointer<QAction> m_capslockAction;
QPointer<PasswordEdit> m_repeatPasswordEdit;
QPointer<PasswordEdit> m_parentPasswordEdit;
bool m_sendGeneratorSignal = false;
bool m_isRepeatPartner = false;
bool m_capslockState = false;
QPointer<QTimer> m_capslockPollTimer;
};

#endif // KEEPASSX_PASSWORDEDIT_H
1 change: 1 addition & 0 deletions src/gui/osutils/OSUtilsBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class OSUtilsBase : public QObject

public:
virtual bool isDarkMode() = 0;
virtual bool isCapslockEnabled() = 0;

protected:
explicit OSUtilsBase(QObject* parent = nullptr);
Expand Down
8 changes: 8 additions & 0 deletions src/gui/osutils/macutils/MacUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include "MacUtils.h"
#include <QApplication>

#include <CoreGraphics/CGEventSource.h>


QPointer<MacUtils> MacUtils::m_instance = nullptr;

MacUtils::MacUtils(QObject* parent)
Expand Down Expand Up @@ -85,3 +88,8 @@ bool MacUtils::enableScreenRecording()
{
return m_appkit->enableScreenRecording();
}

bool MacUtils::isCapslockEnabled()
{
return ((CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState) & kCGEventFlagMaskAlphaShift) != 0);
}
4 changes: 3 additions & 1 deletion src/gui/osutils/macutils/MacUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ class MacUtils : public OSUtilsBase
public:
static MacUtils* instance();

bool isDarkMode() override;
bool isCapslockEnabled() override;

WId activeWindow();
bool raiseWindow(WId pid);
bool raiseLastActiveWindow();
bool raiseOwnWindow();
bool hideOwnWindow();
bool isHidden();
bool isDarkMode() override;
bool enableAccessibility();
bool enableScreenRecording();

Expand Down
29 changes: 29 additions & 0 deletions src/gui/osutils/nixutils/NixUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@
#include "NixUtils.h"
#include <QApplication>
#include <QColor>
#include <QGuiApplication>
#include <QPalette>
#include <QStyle>

// namespace required to avoid name clashes with declarations in XKBlib.h
#include <qpa/qplatformnativeinterface.h>
namespace X11
{
#include <X11/XKBlib.h>
}

QPointer<NixUtils> NixUtils::m_instance = nullptr;

NixUtils* NixUtils::instance()
Expand Down Expand Up @@ -48,3 +56,24 @@ bool NixUtils::isDarkMode()
}
return qApp->style()->standardPalette().color(QPalette::Window).toHsl().lightness() < 110;
}

bool NixUtils::isCapslockEnabled()
{
QPlatformNativeInterface* native = QGuiApplication::platformNativeInterface();
auto* display = native->nativeResourceForWindow("display", nullptr);
if (!display) {
return false;
}

QString platform = QGuiApplication::platformName();
if (platform == "xcb") {
unsigned state = 0;
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
return ((state & 1u) != 0);
}
}

// TODO: Wayland

return false;
}
1 change: 1 addition & 0 deletions src/gui/osutils/nixutils/NixUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class NixUtils : public OSUtilsBase
static NixUtils* instance();

bool isDarkMode() override;
bool isCapslockEnabled() override;

private:
explicit NixUtils(QObject* parent = nullptr);
Expand Down
5 changes: 5 additions & 0 deletions src/gui/osutils/winutils/WinUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ bool WinUtils::isDarkMode()
QSettings::NativeFormat);
return settings.value("AppsUseLightTheme", 1).toInt() == 0;
}

bool WinUtils::isCapslockEnabled()
{
return (GetKeyState(VK_CAPITAL) == 1);
}
1 change: 1 addition & 0 deletions src/gui/osutils/winutils/WinUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class WinUtils : public OSUtilsBase
static void registerEventFilters();

bool isDarkMode() override;
bool isCapslockEnabled() override;

protected:
explicit WinUtils(QObject* parent = nullptr);
Expand Down

0 comments on commit 089fe91

Please sign in to comment.