0
0
mirror of https://github.com/keepassxreboot/keepassxc.git synced 2024-09-19 20:02:18 +02:00

Move global shortcut handling into OSUtils (#5566)

Move global shortcut handling into OSUtils
This commit is contained in:
Jonathan White 2020-12-13 23:23:25 -05:00 committed by GitHub
parent a6f01349e8
commit 404fd941e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1124 additions and 1136 deletions

View File

@ -305,11 +305,11 @@ if(WITH_COVERAGE AND CMAKE_COMPILER_IS_CLANGXX)
# `find src -iname '*.h' -or -iname '*.cpp'`
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(UNIX AND NOT APPLE)
check_add_gcc_compiler_flag("-Qunused-arguments")
add_gcc_compiler_flags("-pie -fPIE")
check_add_gcc_compiler_flag("-fPIC")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now -pie")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
endif()
@ -399,7 +399,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)

View File

@ -206,7 +206,8 @@ if(UNIX AND NOT APPLE)
${keepassx_SOURCES}
gui/MainWindowAdaptor.cpp
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
gui/osutils/nixutils/NixUtils.cpp)
gui/osutils/nixutils/NixUtils.cpp
gui/osutils/nixutils/X11Funcs.cpp)
endif()
if(MINGW)
set(keepassx_SOURCES
@ -214,11 +215,6 @@ if(MINGW)
gui/osutils/winutils/ScreenLockListenerWin.cpp
gui/osutils/winutils/WinUtils.cpp)
endif()
if(MINGW OR (UNIX AND NOT APPLE))
set(keepassx_SOURCES
${keepassx_SOURCES}
gui/osutils/OSEventFilter.cpp)
endif()
set(keepassx_SOURCES ${keepassx_SOURCES}
../share/icons/icons.qrc
@ -343,7 +339,7 @@ if(WITH_XC_KEESHARE)
endif()
if(APPLE)
target_link_libraries(keepassx_core "-framework Foundation -framework AppKit")
target_link_libraries(keepassx_core "-framework Foundation -framework AppKit -framework Carbon")
if(Qt5MacExtras_FOUND)
target_link_libraries(keepassx_core Qt5::MacExtras)
endif()
@ -356,7 +352,7 @@ if(HAIKU)
target_link_libraries(keepassx_core network)
endif()
if(UNIX AND NOT APPLE)
target_link_libraries(keepassx_core Qt5::DBus X11)
target_link_libraries(keepassx_core Qt5::DBus Qt5::X11Extras X11)
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif()
if(MINGW)

View File

@ -37,18 +37,13 @@
#include "core/Tools.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
#ifdef Q_OS_MAC
#include "gui/osutils/macutils/MacUtils.h"
#endif
#include "gui/osutils/OSUtils.h"
AutoType* AutoType::m_instance = nullptr;
AutoType::AutoType(QObject* parent, bool test)
: QObject(parent)
, m_autoTypeDelay(0)
, m_currentGlobalKey(static_cast<Qt::Key>(0))
, m_currentGlobalModifiers(nullptr)
, m_pluginLoader(new QPluginLoader(this))
, m_plugin(nullptr)
, m_executor(nullptr)
@ -96,7 +91,11 @@ void AutoType::loadPlugin(const QString& pluginPath)
if (m_plugin) {
if (m_plugin->isAvailable()) {
m_executor = m_plugin->createExecutor();
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType()));
connect(osUtils, &OSUtilsBase::globalShortcutTriggered, this, [this](QString name) {
if (name == "autotype") {
startGlobalAutoType();
}
});
} else {
unloadPlugin();
}
@ -153,44 +152,18 @@ void AutoType::raiseWindow()
#endif
}
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
{
Q_ASSERT(key);
Q_ASSERT(modifiers);
if (!m_plugin) {
return false;
}
if (key != m_currentGlobalKey || modifiers != m_currentGlobalModifiers) {
if (m_currentGlobalKey && m_currentGlobalModifiers) {
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
}
if (m_plugin->registerGlobalShortcut(key, modifiers)) {
m_currentGlobalKey = key;
m_currentGlobalModifiers = modifiers;
return true;
}
return false;
}
return true;
return osUtils->registerGlobalShortcut("autotype", key, modifiers, error);
}
void AutoType::unregisterGlobalShortcut()
{
if (m_plugin && m_currentGlobalKey && m_currentGlobalModifiers) {
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
}
}
int AutoType::callEventFilter(void* event)
{
if (!m_plugin) {
return -1;
}
return m_plugin->platformEventFilter(event);
osUtils->unregisterGlobalShortcut("autotype");
}
/**
@ -303,6 +276,23 @@ void AutoType::startGlobalAutoType()
m_windowForGlobal = m_plugin->activeWindow();
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
#ifdef Q_OS_MACOS
// Determine if the user has given proper permissions to KeePassXC to perform Auto-Type
static bool accessibilityChecked = false;
if (!accessibilityChecked) {
if (macUtils()->enableAccessibility() && macUtils()->enableScreenRecording()) {
accessibilityChecked = true;
} else if (getMainWindow()) {
// Does not have required permissions to Auto-Type, ignore the event
MessageBox::information(
nullptr,
tr("Permission Required"),
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
"already granted permission, you may have to restart KeePassXC."));
return;
}
}
m_windowState = WindowState::Normal;
if (getMainWindow()) {
if (getMainWindow()->isMinimized()) {

View File

@ -39,9 +39,8 @@ class AutoType : public QObject
public:
QStringList windowTitles();
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
void unregisterGlobalShortcut();
int callEventFilter(void* event);
static bool checkSyntax(const QString& string);
static bool checkHighRepetition(const QString& string);
static bool checkSlowKeypress(const QString& string);
@ -99,8 +98,6 @@ private:
QMutex m_inAutoType;
QMutex m_inGlobalAutoTypeDialog;
int m_autoTypeDelay;
Qt::Key m_currentGlobalKey;
Qt::KeyboardModifiers m_currentGlobalModifiers;
QPluginLoader* m_pluginLoader;
AutoTypePlatformInterface* m_plugin;
AutoTypeExecutor* m_executor;

View File

@ -32,9 +32,6 @@ public:
virtual QStringList windowTitles() = 0;
virtual WId activeWindow() = 0;
virtual QString activeWindowTitle() = 0;
virtual bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
virtual void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
virtual int platformEventFilter(void* event) = 0;
virtual bool raiseWindow(WId window) = 0;
virtual void unload()
{

View File

@ -18,6 +18,7 @@
#include "ShortcutWidget.h"
#include <QKeyEvent>
#include <QToolTip>
#include "autotype/AutoType.h"
@ -48,9 +49,11 @@ void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
displayShortcut(m_key, m_modifiers);
if (autoType()->registerGlobalShortcut(m_key, m_modifiers)) {
QString error;
if (autoType()->registerGlobalShortcut(m_key, m_modifiers, &error)) {
setStyleSheet("");
} else {
QToolTip::showText(mapToGlobal(rect().bottomLeft()), error);
setStyleSheet("background-color: #FF9696;");
}
}

View File

@ -22,24 +22,12 @@
#include <ApplicationServices/ApplicationServices.h>
#define HOTKEY_ID 1
#define MAX_WINDOW_TITLE_LENGTH 1024
#define INVALID_KEYCODE 0xFFFF
namespace {
bool accessibilityChecked = false;
}
AutoTypePlatformMac::AutoTypePlatformMac()
: m_hotkeyRef(nullptr)
, m_hotkeyId({ 'kpx2', HOTKEY_ID })
{
EventTypeSpec eventSpec;
eventSpec.eventClass = kEventClassKeyboard;
eventSpec.eventKind = kEventHotKeyPressed;
MessageBox::initializeButtonDefs();
::InstallApplicationEventHandler(AutoTypePlatformMac::hotkeyHandler, 1, &eventSpec, this, nullptr);
}
/**
@ -120,44 +108,6 @@ QString AutoTypePlatformMac::activeWindowTitle()
return title;
}
//
// Register global hotkey
//
bool AutoTypePlatformMac::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
uint16 nativeKeyCode = qtToNativeKeyCode(key);
if (nativeKeyCode == INVALID_KEYCODE) {
qWarning("Invalid key code");
return false;
}
CGEventFlags nativeModifiers = qtToNativeModifiers(modifiers, false);
if (::RegisterEventHotKey(nativeKeyCode, nativeModifiers, m_hotkeyId, GetApplicationEventTarget(), 0, &m_hotkeyRef) != noErr) {
qWarning("Register hotkey failed");
return false;
}
return true;
}
//
// Unregister global hotkey
//
void AutoTypePlatformMac::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(key);
Q_UNUSED(modifiers);
::UnregisterEventHotKey(m_hotkeyRef);
}
int AutoTypePlatformMac::platformEventFilter(void* event)
{
Q_UNUSED(event);
Q_ASSERT(false);
return -1;
}
AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
{
return new AutoTypeExecutorMac(this);
@ -208,13 +158,13 @@ void AutoTypePlatformMac::sendChar(const QChar& ch, bool isKeyDown)
//
void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers = 0)
{
uint16 keyCode = qtToNativeKeyCode(key);
uint16 keyCode = macUtils()->qtToNativeKeyCode(key);
if (keyCode == INVALID_KEYCODE) {
return;
}
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, keyCode, isKeyDown);
CGEventFlags nativeModifiers = qtToNativeModifiers(modifiers, true);
CGEventFlags nativeModifiers = macUtils()->qtToNativeModifiers(modifiers, true);
if (keyEvent != nullptr) {
::CGEventSetFlags(keyEvent, nativeModifiers);
::CGEventPost(kCGSessionEventTap, keyEvent);
@ -222,223 +172,6 @@ void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModif
}
}
//
// Translate qt key code to mac os key code
// see: HIToolbox/Events.h
//
uint16 AutoTypePlatformMac::qtToNativeKeyCode(Qt::Key key)
{
switch (key) {
case Qt::Key_A:
return kVK_ANSI_A;
case Qt::Key_B:
return kVK_ANSI_B;
case Qt::Key_C:
return kVK_ANSI_C;
case Qt::Key_D:
return kVK_ANSI_D;
case Qt::Key_E:
return kVK_ANSI_E;
case Qt::Key_F:
return kVK_ANSI_F;
case Qt::Key_G:
return kVK_ANSI_G;
case Qt::Key_H:
return kVK_ANSI_H;
case Qt::Key_I:
return kVK_ANSI_I;
case Qt::Key_J:
return kVK_ANSI_J;
case Qt::Key_K:
return kVK_ANSI_K;
case Qt::Key_L:
return kVK_ANSI_L;
case Qt::Key_M:
return kVK_ANSI_M;
case Qt::Key_N:
return kVK_ANSI_N;
case Qt::Key_O:
return kVK_ANSI_O;
case Qt::Key_P:
return kVK_ANSI_P;
case Qt::Key_Q:
return kVK_ANSI_Q;
case Qt::Key_R:
return kVK_ANSI_R;
case Qt::Key_S:
return kVK_ANSI_S;
case Qt::Key_T:
return kVK_ANSI_T;
case Qt::Key_U:
return kVK_ANSI_U;
case Qt::Key_V:
return kVK_ANSI_V;
case Qt::Key_W:
return kVK_ANSI_W;
case Qt::Key_X:
return kVK_ANSI_X;
case Qt::Key_Y:
return kVK_ANSI_Y;
case Qt::Key_Z:
return kVK_ANSI_Z;
case Qt::Key_0:
return kVK_ANSI_0;
case Qt::Key_1:
return kVK_ANSI_1;
case Qt::Key_2:
return kVK_ANSI_2;
case Qt::Key_3:
return kVK_ANSI_3;
case Qt::Key_4:
return kVK_ANSI_4;
case Qt::Key_5:
return kVK_ANSI_5;
case Qt::Key_6:
return kVK_ANSI_6;
case Qt::Key_7:
return kVK_ANSI_7;
case Qt::Key_8:
return kVK_ANSI_8;
case Qt::Key_9:
return kVK_ANSI_9;
case Qt::Key_Equal:
return kVK_ANSI_Equal;
case Qt::Key_Minus:
return kVK_ANSI_Minus;
case Qt::Key_BracketRight:
return kVK_ANSI_RightBracket;
case Qt::Key_BracketLeft:
return kVK_ANSI_LeftBracket;
case Qt::Key_QuoteDbl:
return kVK_ANSI_Quote;
case Qt::Key_Semicolon:
return kVK_ANSI_Semicolon;
case Qt::Key_Backslash:
return kVK_ANSI_Backslash;
case Qt::Key_Comma:
return kVK_ANSI_Comma;
case Qt::Key_Slash:
return kVK_ANSI_Slash;
case Qt::Key_Period:
return kVK_ANSI_Period;
case Qt::Key_Shift:
return kVK_Shift;
case Qt::Key_Control:
return kVK_Command;
case Qt::Key_Backspace:
return kVK_Delete;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return kVK_Tab;
case Qt::Key_Enter:
case Qt::Key_Return:
return kVK_Return;
case Qt::Key_CapsLock:
return kVK_CapsLock;
case Qt::Key_Escape:
return kVK_Escape;
case Qt::Key_Space:
return kVK_Space;
case Qt::Key_PageUp:
return kVK_PageUp;
case Qt::Key_PageDown:
return kVK_PageDown;
case Qt::Key_End:
return kVK_End;
case Qt::Key_Home:
return kVK_Home;
case Qt::Key_Left:
return kVK_LeftArrow;
case Qt::Key_Up:
return kVK_UpArrow;
case Qt::Key_Right:
return kVK_RightArrow;
case Qt::Key_Down:
return kVK_DownArrow;
case Qt::Key_Delete:
return kVK_ForwardDelete;
case Qt::Key_Help:
return kVK_Help;
case Qt::Key_F1:
return kVK_F1;
case Qt::Key_F2:
return kVK_F2;
case Qt::Key_F3:
return kVK_F3;
case Qt::Key_F4:
return kVK_F4;
case Qt::Key_F5:
return kVK_F5;
case Qt::Key_F6:
return kVK_F6;
case Qt::Key_F7:
return kVK_F7;
case Qt::Key_F8:
return kVK_F8;
case Qt::Key_F9:
return kVK_F9;
case Qt::Key_F10:
return kVK_F10;
case Qt::Key_F11:
return kVK_F11;
case Qt::Key_F12:
return kVK_F12;
case Qt::Key_F13:
return kVK_F13;
case Qt::Key_F14:
return kVK_F14;
case Qt::Key_F15:
return kVK_F15;
case Qt::Key_F16:
return kVK_F16;
default:
Q_ASSERT(false);
return INVALID_KEYCODE;
}
}
//
// Translate qt key modifiers to mac os modifiers
// see: https://doc.qt.io/qt-5/osx-issues.html#special-keys
//
CGEventFlags AutoTypePlatformMac::qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native)
{
CGEventFlags nativeModifiers = CGEventFlags(0);
CGEventFlags shiftMod = CGEventFlags(shiftKey);
CGEventFlags cmdMod = CGEventFlags(cmdKey);
CGEventFlags optionMod = CGEventFlags(optionKey);
CGEventFlags controlMod = CGEventFlags(controlKey);
if (native) {
shiftMod = kCGEventFlagMaskShift;
cmdMod = kCGEventFlagMaskCommand;
optionMod = kCGEventFlagMaskAlternate;
controlMod = kCGEventFlagMaskControl;
}
if (modifiers & Qt::ShiftModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | shiftMod);
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | cmdMod);
}
if (modifiers & Qt::AltModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | optionMod);
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | controlMod);
}
return nativeModifiers;
}
//
// Get window layer/level
//
@ -472,39 +205,6 @@ QString AutoTypePlatformMac::windowTitle(CFDictionaryRef window)
return title;
}
//
// Carbon hotkey handler
//
OSStatus AutoTypePlatformMac::hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
Q_UNUSED(nextHandler);
// Determine if the user has given proper permissions to KeePassXC to perform Auto-Type
if (!accessibilityChecked) {
if (macUtils()->enableAccessibility() && macUtils()->enableScreenRecording()) {
accessibilityChecked = true;
} else {
// Does not have required permissions to Auto-Type, ignore the keypress
MessageBox::information(nullptr,
tr("Permission Required"),
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
"already granted permission, you may have to restart KeePassXC."));
return noErr;
}
}
AutoTypePlatformMac* self = static_cast<AutoTypePlatformMac*>(userData);
EventHotKeyID hotkeyId;
if (::GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId) == noErr
&& hotkeyId.id == HOTKEY_ID) {
emit self->globalShortcutTriggered();
}
return noErr;
}
//
// ------------------------------ AutoTypeExecutorMac ------------------------------
//

View File

@ -38,9 +38,6 @@ public:
QStringList windowTitles() override;
WId activeWindow() override;
QString activeWindowTitle() override;
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
int platformEventFilter(void* event) override;
bool raiseWindow(WId pid) override;
AutoTypeExecutor* createExecutor() override;
@ -50,18 +47,9 @@ public:
void sendChar(const QChar& ch, bool isKeyDown);
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers);
signals:
void globalShortcutTriggered();
private:
EventHotKeyRef m_hotkeyRef;
EventHotKeyID m_hotkeyId;
static uint16 qtToNativeKeyCode(Qt::Key key);
static CGEventFlags qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native);
static int windowLayer(CFDictionaryRef window);
static QString windowTitle(CFDictionaryRef window);
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
};
class AutoTypeExecutorMac : public AutoTypeExecutor

View File

@ -42,37 +42,11 @@ QString AutoTypePlatformTest::activeWindowTitle()
return m_activeWindowTitle;
}
bool AutoTypePlatformTest::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(key);
Q_UNUSED(modifiers);
return true;
}
void AutoTypePlatformTest::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(key);
Q_UNUSED(modifiers);
}
int AutoTypePlatformTest::platformEventFilter(void* event)
{
Q_UNUSED(event);
return -1;
}
AutoTypeExecutor* AutoTypePlatformTest::createExecutor()
{
return new AutoTypeExecutorTest(this);
}
void AutoTypePlatformTest::triggerGlobalAutoType()
{
emit globalShortcutTriggered();
}
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
{
m_activeWindowTitle = title;

View File

@ -37,9 +37,6 @@ public:
QStringList windowTitles() override;
WId activeWindow() override;
QString activeWindowTitle() override;
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
int platformEventFilter(void* event) override;
bool raiseWindow(WId window) override;
AutoTypeExecutor* createExecutor() override;
@ -48,7 +45,6 @@ public:
bool raiseOwnWindow() override;
#endif
void triggerGlobalAutoType() override;
void setActiveWindowTitle(const QString& title) override;
QString actionChars() override;
@ -58,9 +54,6 @@ public:
void addActionChar(AutoTypeChar* action);
void addActionKey(AutoTypeKey* action);
signals:
void globalShortcutTriggered();
private:
QString m_activeWindowTitle;
QList<AutoTypeAction*> m_actionList;

View File

@ -26,7 +26,6 @@ public:
virtual ~AutoTypeTestInterface()
{
}
virtual void triggerGlobalAutoType() = 0;
virtual void setActiveWindowTitle(const QString& title) = 0;
virtual QString actionChars() = 0;

View File

@ -17,6 +17,7 @@
*/
#include "AutoTypeWindows.h"
#include "gui/osutils/OSUtils.h"
#include <VersionHelpers.h>
@ -61,49 +62,6 @@ QString AutoTypePlatformWin::activeWindowTitle()
return windowTitle(::GetForegroundWindow());
}
//
// Register global hotkey
//
bool AutoTypePlatformWin::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
DWORD nativeKeyCode = qtToNativeKeyCode(key);
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
return false;
}
DWORD nativeModifiers = qtToNativeModifiers(modifiers);
if (!::RegisterHotKey(nullptr, HOTKEY_ID, nativeModifiers | MOD_NOREPEAT, nativeKeyCode)) {
return false;
}
return true;
}
//
// Unregister global hotkey
//
void AutoTypePlatformWin::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
Q_UNUSED(key);
Q_UNUSED(modifiers);
::UnregisterHotKey(nullptr, HOTKEY_ID);
}
//
// Native event filter
//
int AutoTypePlatformWin::platformEventFilter(void* event)
{
MSG* msg = static_cast<MSG*>(event);
if (msg->message == WM_HOTKEY && msg->wParam == HOTKEY_ID) {
emit globalShortcutTriggered();
return 1;
}
return -1;
}
AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
{
return new AutoTypeExecutorWin(this);
@ -145,7 +103,7 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
//
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
{
DWORD nativeKeyCode = qtToNativeKeyCode(key);
DWORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
return;
}
@ -168,234 +126,12 @@ void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
::SendInput(1, &in, sizeof(INPUT));
}
// clang-format off
//
// Translate qt key code to windows virtual key code
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
//
DWORD AutoTypePlatformWin::qtToNativeKeyCode(Qt::Key key)
{
switch (key) {
case Qt::Key_Backspace:
return VK_BACK; // 0x08
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB; // 0x09
case Qt::Key_Clear:
return VK_CLEAR; // 0x0C
case Qt::Key_Enter:
case Qt::Key_Return:
return VK_RETURN; // 0x0D
case Qt::Key_Shift:
return VK_SHIFT; // 0x10
case Qt::Key_Control:
return VK_CONTROL; // 0x11
case Qt::Key_Pause:
return VK_PAUSE; // 0x13
case Qt::Key_CapsLock:
return VK_CAPITAL; // 0x14
case Qt::Key_Escape:
return VK_ESCAPE; // 0x1B
case Qt::Key_Space:
return VK_SPACE; // 0x20
case Qt::Key_PageUp:
return VK_PRIOR; // 0x21
case Qt::Key_PageDown:
return VK_NEXT; // 0x22
case Qt::Key_End:
return VK_END; // 0x23
case Qt::Key_Home:
return VK_HOME; // 0x24
case Qt::Key_Left:
return VK_LEFT; // 0x25
case Qt::Key_Up:
return VK_UP; // 0x26
case Qt::Key_Right:
return VK_RIGHT; // 0x27
case Qt::Key_Down:
return VK_DOWN; // 0x28
case Qt::Key_Print:
return VK_SNAPSHOT; // 0x2C
case Qt::Key_Insert:
return VK_INSERT; // 0x2D
case Qt::Key_Delete:
return VK_DELETE; // 0x2E
case Qt::Key_Help:
return VK_HELP; // 0x2F
case Qt::Key_0:
return 0x30; // 0x30
case Qt::Key_1:
return 0x31; // 0x31
case Qt::Key_2:
return 0x32; // 0x32
case Qt::Key_3:
return 0x33; // 0x33
case Qt::Key_4:
return 0x34; // 0x34
case Qt::Key_5:
return 0x35; // 0x35
case Qt::Key_6:
return 0x36; // 0x36
case Qt::Key_7:
return 0x37; // 0x37
case Qt::Key_8:
return 0x38; // 0x38
case Qt::Key_9:
return 0x39; // 0x39
case Qt::Key_A:
return 0x41; // 0x41
case Qt::Key_B:
return 0x42; // 0x42
case Qt::Key_C:
return 0x43; // 0x43
case Qt::Key_D:
return 0x44; // 0x44
case Qt::Key_E:
return 0x45; // 0x45
case Qt::Key_F:
return 0x46; // 0x46
case Qt::Key_G:
return 0x47; // 0x47
case Qt::Key_H:
return 0x48; // 0x48
case Qt::Key_I:
return 0x49; // 0x49
case Qt::Key_J:
return 0x4A; // 0x4A
case Qt::Key_K:
return 0x4B; // 0x4B
case Qt::Key_L:
return 0x4C; // 0x4C
case Qt::Key_M:
return 0x4D; // 0x4D
case Qt::Key_N:
return 0x4E; // 0x4E
case Qt::Key_O:
return 0x4F; // 0x4F
case Qt::Key_P:
return 0x50; // 0x50
case Qt::Key_Q:
return 0x51; // 0x51
case Qt::Key_R:
return 0x52; // 0x52
case Qt::Key_S:
return 0x53; // 0x53
case Qt::Key_T:
return 0x54; // 0x54
case Qt::Key_U:
return 0x55; // 0x55
case Qt::Key_V:
return 0x56; // 0x56
case Qt::Key_W:
return 0x57; // 0x57
case Qt::Key_X:
return 0x58; // 0x58
case Qt::Key_Y:
return 0x59; // 0x59
case Qt::Key_Z:
return 0x5A; // 0x5A
case Qt::Key_F1:
return VK_F1; // 0x70
case Qt::Key_F2:
return VK_F2; // 0x71
case Qt::Key_F3:
return VK_F3; // 0x72
case Qt::Key_F4:
return VK_F4; // 0x73
case Qt::Key_F5:
return VK_F5; // 0x74
case Qt::Key_F6:
return VK_F6; // 0x75
case Qt::Key_F7:
return VK_F7; // 0x76
case Qt::Key_F8:
return VK_F8; // 0x77
case Qt::Key_F9:
return VK_F9; // 0x78
case Qt::Key_F10:
return VK_F10; // 0x79
case Qt::Key_F11:
return VK_F11; // 0x7A
case Qt::Key_F12:
return VK_F12; // 0x7B
case Qt::Key_F13:
return VK_F13; // 0x7C
case Qt::Key_F14:
return VK_F14; // 0x7D
case Qt::Key_F15:
return VK_F15; // 0x7E
case Qt::Key_F16:
return VK_F16; // 0x7F
case Qt::Key_F17:
return VK_F17; // 0x80
case Qt::Key_F18:
return VK_F18; // 0x81
case Qt::Key_F19:
return VK_F19; // 0x82
case Qt::Key_F20:
return VK_F20; // 0x83
case Qt::Key_F21:
return VK_F21; // 0x84
case Qt::Key_F22:
return VK_F22; // 0x85
case Qt::Key_F23:
return VK_F23; // 0x86
case Qt::Key_F24:
return VK_F24; // 0x87
case Qt::Key_NumLock:
return VK_NUMLOCK; // 0x90
case Qt::Key_ScrollLock:
return VK_SCROLL; // 0x91
case Qt::Key_Exclam: // !
case Qt::Key_QuoteDbl: // "
case Qt::Key_NumberSign: // #
case Qt::Key_Dollar: // $
case Qt::Key_Percent: // %
case Qt::Key_Ampersand: // &
case Qt::Key_Apostrophe: // '
case Qt::Key_ParenLeft: // (
case Qt::Key_ParenRight: // )
case Qt::Key_Asterisk: // *
case Qt::Key_Plus: // +
case Qt::Key_Comma: // ,
case Qt::Key_Minus: // -
case Qt::Key_Period: // .
case Qt::Key_Slash: // /
case Qt::Key_Colon: // :
case Qt::Key_Semicolon: // ;
case Qt::Key_Less: // <
case Qt::Key_Equal: // =
case Qt::Key_Greater: // >
case Qt::Key_Question: // ?
case Qt::Key_BracketLeft: // [
case Qt::Key_Backslash: // '\'
case Qt::Key_BracketRight: // ]
case Qt::Key_AsciiCircum: // ^
case Qt::Key_Underscore: // _
case Qt::Key_QuoteLeft: // `
case Qt::Key_BraceLeft: // {
case Qt::Key_Bar: // |
case Qt::Key_BraceRight: // }
case Qt::Key_AsciiTilde: // ~
return LOBYTE(::VkKeyScanExW(key, ::GetKeyboardLayout(0)));
default:
Q_ASSERT(false);
return 0;
}
}
//
// The extended-key flag indicates whether the keystroke message originated
// from one of the additional keys on the enhanced keyboard
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx#EXTENDED_KEY_FLAG
//
BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
bool AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
{
switch (nativeKeyCode) {
case VK_RMENU:
@ -417,44 +153,21 @@ BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
case VK_LWIN:
case VK_RWIN:
case VK_APPS:
return TRUE;
return true;
default:
return FALSE;
return false;
}
}
// clang-format on
//
// Translate qt key modifiers to windows modifiers
//
DWORD AutoTypePlatformWin::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
{
DWORD nativeModifiers = 0;
if (modifiers & Qt::ShiftModifier) {
nativeModifiers |= MOD_SHIFT;
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers |= MOD_CONTROL;
}
if (modifiers & Qt::AltModifier) {
nativeModifiers |= MOD_ALT;
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers |= MOD_WIN;
}
return nativeModifiers;
}
//
// Test if window is in Alt+Tab list
// see: https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863
//
BOOL AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
bool AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
{
if (!::IsWindowVisible(hwnd)) {
return FALSE;
return false;
}
// Start at the root owner

View File

@ -20,7 +20,7 @@
#define KEEPASSX_AUTOTYPEWINDOWS_H
#include <QtPlugin>
#include <Windows.h>
#include <windows.h>
#include "autotype/AutoTypeAction.h"
#include "autotype/AutoTypePlatformPlugin.h"
@ -36,23 +36,15 @@ public:
QStringList windowTitles() override;
WId activeWindow() override;
QString activeWindowTitle() override;
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
int platformEventFilter(void* event) override;
bool raiseWindow(WId window) override;
AutoTypeExecutor* createExecutor() override;
void sendChar(const QChar& ch, bool isKeyDown);
void sendKey(Qt::Key key, bool isKeyDown);
signals:
void globalShortcutTriggered();
private:
static DWORD qtToNativeKeyCode(Qt::Key key);
static DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
static BOOL isExtendedKey(DWORD nativeKeyCode);
static BOOL isAltTabWindow(HWND hwnd);
static bool isExtendedKey(DWORD nativeKeyCode);
static bool isAltTabWindow(HWND hwnd);
static BOOL CALLBACK windowTitleEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam);
static QString windowTitle(HWND hwnd);
};

View File

@ -18,15 +18,6 @@
*/
#include "AutoTypeXCB.h"
#include "KeySymMap.h"
#include "core/Tools.h"
#include <time.h>
#include <xcb/xcb.h>
bool AutoTypePlatformX11::m_catchXErrors = false;
bool AutoTypePlatformX11::m_xErrorOccurred = false;
int (*AutoTypePlatformX11::m_oldXErrorHandler)(Display*, XErrorEvent*) = nullptr;
AutoTypePlatformX11::AutoTypePlatformX11()
{
@ -49,17 +40,14 @@ AutoTypePlatformX11::AutoTypePlatformX11()
m_classBlacklist << "xfdesktop"
<< "xfce4-panel"; // Xfce 4
m_currentGlobalKey = static_cast<Qt::Key>(0);
m_currentGlobalModifiers = nullptr;
m_keysymTable = nullptr;
m_xkb = nullptr;
m_remapKeycode = 0;
m_currentRemapKeysym = NoSymbol;
m_modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
m_loaded = true;
connect(nixUtils(), &NixUtils::keymapChanged, this, [this] { updateKeymap(); });
updateKeymap();
}
@ -142,105 +130,6 @@ QString AutoTypePlatformX11::activeWindowTitle()
return windowTitle(activeWindow(), true);
}
bool AutoTypePlatformX11::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
int keycode = XKeysymToKeycode(m_dpy, charToKeySym(key));
uint nativeModifiers = qtToNativeModifiers(modifiers);
startCatchXErrors();
XGrabKey(m_dpy, keycode, nativeModifiers, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(m_dpy, keycode, nativeModifiers | LockMask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask | LockMask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
stopCatchXErrors();
if (!m_xErrorOccurred) {
m_currentGlobalKey = key;
m_currentGlobalModifiers = modifiers;
m_currentGlobalKeycode = keycode;
m_currentGlobalNativeModifiers = nativeModifiers;
return true;
} else {
unregisterGlobalShortcut(key, modifiers);
return false;
}
}
uint AutoTypePlatformX11::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
{
uint nativeModifiers = 0;
if (modifiers & Qt::ShiftModifier) {
nativeModifiers |= ShiftMask;
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers |= ControlMask;
}
if (modifiers & Qt::AltModifier) {
nativeModifiers |= Mod1Mask;
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers |= Mod4Mask;
}
return nativeModifiers;
}
void AutoTypePlatformX11::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
KeyCode keycode = XKeysymToKeycode(m_dpy, charToKeySym(key));
uint nativeModifiers = qtToNativeModifiers(modifiers);
XUngrabKey(m_dpy, keycode, nativeModifiers, m_rootWindow);
XUngrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask, m_rootWindow);
XUngrabKey(m_dpy, keycode, nativeModifiers | LockMask, m_rootWindow);
XUngrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask | LockMask, m_rootWindow);
m_currentGlobalKey = static_cast<Qt::Key>(0);
m_currentGlobalModifiers = nullptr;
m_currentGlobalKeycode = 0;
m_currentGlobalNativeModifiers = 0;
}
int AutoTypePlatformX11::platformEventFilter(void* event)
{
xcb_generic_event_t* genericEvent = static_cast<xcb_generic_event_t*>(event);
quint8 type = genericEvent->response_type & 0x7f;
if (type == XCB_KEY_PRESS || type == XCB_KEY_RELEASE) {
xcb_key_press_event_t* keyPressEvent = static_cast<xcb_key_press_event_t*>(event);
if (keyPressEvent->detail == m_currentGlobalKeycode
&& (keyPressEvent->state & m_modifierMask) == m_currentGlobalNativeModifiers
&& (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized()) && m_loaded) {
if (type == XCB_KEY_PRESS) {
emit globalShortcutTriggered();
}
return 1;
}
} else if (type == XCB_MAPPING_NOTIFY) {
xcb_mapping_notify_event_t* mappingNotifyEvent = static_cast<xcb_mapping_notify_event_t*>(event);
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD
|| mappingNotifyEvent->request == XCB_MAPPING_MODIFIER) {
XMappingEvent xMappingEvent;
memset(&xMappingEvent, 0, sizeof(xMappingEvent));
xMappingEvent.type = MappingNotify;
xMappingEvent.display = m_dpy;
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD) {
xMappingEvent.request = MappingKeyboard;
} else {
xMappingEvent.request = MappingModifier;
}
xMappingEvent.first_keycode = mappingNotifyEvent->first_keycode;
xMappingEvent.count = mappingNotifyEvent->count;
XRefreshKeyboardMapping(&xMappingEvent);
updateKeymap();
}
}
return -1;
}
AutoTypeExecutor* AutoTypePlatformX11::createExecutor()
{
return new AutoTypeExecutorX11(this);
@ -395,89 +284,6 @@ bool AutoTypePlatformX11::isTopLevelWindow(Window window)
return result;
}
KeySym AutoTypePlatformX11::charToKeySym(const QChar& ch)
{
ushort unicode = ch.unicode();
/* first check for Latin-1 characters (1:1 mapping) */
if ((unicode >= 0x0020 && unicode <= 0x007e) || (unicode >= 0x00a0 && unicode <= 0x00ff)) {
return unicode;
}
/* mapping table generated from keysymdef.h */
const uint* match = Tools::binaryFind(m_unicodeToKeysymKeys, m_unicodeToKeysymKeys + m_unicodeToKeysymLen, unicode);
int index = match - m_unicodeToKeysymKeys;
if (index != m_unicodeToKeysymLen) {
return m_unicodeToKeysymValues[index];
}
if (unicode >= 0x0100) {
return unicode | 0x01000000;
}
return NoSymbol;
}
KeySym AutoTypePlatformX11::keyToKeySym(Qt::Key key)
{
switch (key) {
case Qt::Key_Tab:
return XK_Tab;
case Qt::Key_Enter:
return XK_Return;
case Qt::Key_Space:
return XK_space;
case Qt::Key_Up:
return XK_Up;
case Qt::Key_Down:
return XK_Down;
case Qt::Key_Left:
return XK_Left;
case Qt::Key_Right:
return XK_Right;
case Qt::Key_Insert:
return XK_Insert;
case Qt::Key_Delete:
return XK_Delete;
case Qt::Key_Home:
return XK_Home;
case Qt::Key_End:
return XK_End;
case Qt::Key_PageUp:
return XK_Page_Up;
case Qt::Key_PageDown:
return XK_Page_Down;
case Qt::Key_Backspace:
return XK_BackSpace;
case Qt::Key_Pause:
return XK_Break;
case Qt::Key_CapsLock:
return XK_Caps_Lock;
case Qt::Key_Escape:
return XK_Escape;
case Qt::Key_Help:
return XK_Help;
case Qt::Key_NumLock:
return XK_Num_Lock;
case Qt::Key_Print:
return XK_Print;
case Qt::Key_ScrollLock:
return XK_Scroll_Lock;
case Qt::Key_Shift:
return XK_Shift_L;
case Qt::Key_Control:
return XK_Control_L;
case Qt::Key_Alt:
return XK_Alt_L;
default:
if (key >= Qt::Key_F1 && key <= Qt::Key_F16) {
return XK_F1 + (key - Qt::Key_F1);
} else {
return NoSymbol;
}
}
}
/*
* Update the keyboard and modifier mapping.
* We need the KeyboardMapping for AddKeysym.
@ -491,8 +297,9 @@ void AutoTypePlatformX11::updateKeymap()
m_xkb = getKeyboard();
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
if (m_keysymTable != nullptr)
if (m_keysymTable != nullptr) {
XFree(m_keysymTable);
}
m_keysymTable = XGetKeyboardMapping(m_dpy, m_minKeycode, m_maxKeycode - m_minKeycode + 1, &m_keysymPerKeycode);
/* determine the keycode to use for remapped keys */
@ -523,11 +330,7 @@ void AutoTypePlatformX11::updateKeymap()
/* Xlib needs some time until the mapping is distributed to
all clients */
// TODO: we should probably only sleep while in the middle of typing something
timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 30 * 1000 * 1000;
nanosleep(&ts, nullptr);
Tools::sleep(30);
}
bool AutoTypePlatformX11::isRemapKeycodeValid()
@ -542,36 +345,6 @@ bool AutoTypePlatformX11::isRemapKeycodeValid()
return false;
}
void AutoTypePlatformX11::startCatchXErrors()
{
Q_ASSERT(!m_catchXErrors);
m_catchXErrors = true;
m_xErrorOccurred = false;
m_oldXErrorHandler = XSetErrorHandler(x11ErrorHandler);
}
void AutoTypePlatformX11::stopCatchXErrors()
{
Q_ASSERT(m_catchXErrors);
XSync(m_dpy, False);
XSetErrorHandler(m_oldXErrorHandler);
m_catchXErrors = false;
}
int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error)
{
Q_UNUSED(display)
Q_UNUSED(error)
if (m_catchXErrors) {
m_xErrorOccurred = true;
}
return 1;
}
XkbDescPtr AutoTypePlatformX11::getKeyboard()
{
int num_devices;
@ -696,7 +469,7 @@ bool AutoTypePlatformX11::keysymModifiers(KeySym keysym, int keycode, unsigned i
* window to simulate keyboard. If modifiers (shift, control, etc)
* are set ON, many events will be sent.
*/
void AutoTypePlatformX11::SendKey(KeySym keysym, unsigned int modifiers)
void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
{
if (keysym == NoSymbol) {
qWarning("No such key: keysym=0x%lX", keysym);
@ -798,12 +571,12 @@ AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform)
void AutoTypeExecutorX11::execChar(AutoTypeChar* action)
{
m_platform->SendKey(m_platform->charToKeySym(action->character));
m_platform->sendKey(qcharToNativeKeyCode(action->character));
}
void AutoTypeExecutorX11::execKey(AutoTypeKey* action)
{
m_platform->SendKey(m_platform->keyToKeySym(action->key));
m_platform->sendKey(qtToNativeKeyCode(action->key));
}
void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
@ -814,13 +587,13 @@ void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
ts.tv_sec = 0;
ts.tv_nsec = 25 * 1000 * 1000;
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_Home), static_cast<unsigned int>(ControlMask));
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_Home), static_cast<unsigned int>(ControlMask));
nanosleep(&ts, nullptr);
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_End), static_cast<unsigned int>(ControlMask | ShiftMask));
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_End), static_cast<unsigned int>(ControlMask | ShiftMask));
nanosleep(&ts, nullptr);
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_Backspace));
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_Backspace));
nanosleep(&ts, nullptr);
}

View File

@ -26,12 +26,16 @@
#include <QX11Info>
#include <QtPlugin>
#include "autotype/AutoTypeAction.h"
#include "autotype/AutoTypePlatformPlugin.h"
#include "core/Tools.h"
#include "gui/osutils/OSUtils.h"
#include "gui/osutils/nixutils/X11Funcs.h"
#include <X11/XKBlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XTest.h>
#include "autotype/AutoTypeAction.h"
#include "autotype/AutoTypePlatformPlugin.h"
#include <xcb/xcb.h>
#define N_MOD_INDICES (Mod5MapIndex + 1)
@ -48,19 +52,10 @@ public:
QStringList windowTitles() override;
WId activeWindow() override;
QString activeWindowTitle() override;
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
int platformEventFilter(void* event) override;
bool raiseWindow(WId window) override;
AutoTypeExecutor* createExecutor() override;
KeySym charToKeySym(const QChar& ch);
KeySym keyToKeySym(Qt::Key key);
void SendKey(KeySym keysym, unsigned int modifiers = 0);
signals:
void globalShortcutTriggered();
void sendKey(KeySym keysym, unsigned int modifiers = 0);
private:
QString windowTitle(Window window, bool useBlacklist);
@ -68,10 +63,6 @@ private:
QString windowClassName(Window window);
QList<Window> widgetsToX11Windows(const QWidgetList& widgetList);
bool isTopLevelWindow(Window window);
uint qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
void startCatchXErrors();
void stopCatchXErrors();
static int x11ErrorHandler(Display* display, XErrorEvent* error);
XkbDescPtr getKeyboard();
void updateKeymap();
@ -94,18 +85,6 @@ private:
Atom m_atomUtf8String;
Atom m_atomNetActiveWindow;
QSet<QString> m_classBlacklist;
Qt::Key m_currentGlobalKey;
Qt::KeyboardModifiers m_currentGlobalModifiers;
uint m_currentGlobalKeycode;
uint m_currentGlobalNativeModifiers;
int m_modifierMask;
static bool m_catchXErrors;
static bool m_xErrorOccurred;
static int (*m_oldXErrorHandler)(Display*, XErrorEvent*);
static const int m_unicodeToKeysymLen;
static const uint m_unicodeToKeysymKeys[];
static const uint m_unicodeToKeysymValues[];
XkbDescPtr m_xkb;
KeySym* m_keysymTable;

View File

@ -36,10 +36,6 @@
#include <QStandardPaths>
#include <QtNetwork/QLocalSocket>
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
#include "gui/osutils/OSEventFilter.h"
#endif
#if defined(Q_OS_UNIX)
#include <signal.h>
#include <sys/socket.h>
@ -60,9 +56,7 @@ Application::Application(int& argc, char** argv)
, m_alreadyRunning(false)
, m_lockFile(nullptr)
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
, m_osEventFilter(new OSEventFilter())
{
installNativeEventFilter(m_osEventFilter.data());
#else
{
#endif
@ -160,6 +154,7 @@ void Application::bootstrap()
QApplication::setFont(QApplication::font("QMessageBox"));
#endif
osUtils->registerNativeEventFilter();
MessageBox::initializeButtonDefs();
#ifdef Q_OS_MACOS

View File

@ -81,9 +81,6 @@ private:
QLockFile* m_lockFile;
QLocalServer m_lockServer;
QString m_socketName;
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
QScopedPointer<OSEventFilter> m_osEventFilter;
#endif
};
#define kpxcApp qobject_cast<Application*>(Application::instance())

View File

@ -60,7 +60,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
m_toggleVisibleAction = new QAction(
icons()->icon("password-show-off"),
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
nullptr);
this);
m_toggleVisibleAction->setCheckable(true);
m_toggleVisibleAction->setShortcut(modifier + Qt::Key_H);
m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut);
@ -70,7 +70,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
m_passwordGeneratorAction = new QAction(
icons()->icon("password-generator"),
tr("Generate Password (%1)").arg(QKeySequence(modifier + Qt::Key_G).toString(QKeySequence::NativeText)),
nullptr);
this);
m_passwordGeneratorAction->setShortcut(modifier + Qt::Key_G);
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
@ -79,7 +79,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
m_capslockAction =
new QAction(icons()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
tr("Warning: Caps Lock enabled!"),
nullptr);
this);
addAction(m_capslockAction, QLineEdit::LeadingPosition);
m_capslockAction->setVisible(false);
}

View File

@ -1,47 +0,0 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "OSEventFilter.h"
#include <QByteArray>
#include "autotype/AutoType.h"
#include "gui/MainWindow.h"
#ifdef Q_OS_WIN
#include <windows.h>
#endif
OSEventFilter::OSEventFilter()
{
}
bool OSEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long* result)
{
Q_UNUSED(result)
#if defined(Q_OS_UNIX)
if (eventType == QByteArrayLiteral("xcb_generic_event_t")) {
#elif defined(Q_OS_WIN)
if (eventType == QByteArrayLiteral("windows_generic_MSG")
|| eventType == QByteArrayLiteral("windows_dispatcher_MSG")) {
#endif
return autoType()->callEventFilter(message) == 1;
}
return false;
}

View File

@ -1,35 +0,0 @@
/*
* Copyright (C) 2013 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OSEVENTFILTER_H
#define OSEVENTFILTER_H
#include <QAbstractNativeEventFilter>
class QByteArray;
class OSEventFilter : public QAbstractNativeEventFilter
{
public:
OSEventFilter();
bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override;
private:
Q_DISABLE_COPY(OSEventFilter)
};
#endif // OSEVENTFILTER_H

View File

@ -50,6 +50,17 @@ public:
*/
virtual bool isCapslockEnabled() = 0;
virtual void registerNativeEventFilter() = 0;
virtual bool registerGlobalShortcut(const QString& name,
Qt::Key key,
Qt::KeyboardModifiers modifiers,
QString* error = nullptr) = 0;
virtual bool unregisterGlobalShortcut(const QString& name) = 0;
signals:
void globalShortcutTriggered(const QString& name);
protected:
explicit OSUtilsBase(QObject* parent = nullptr);
virtual ~OSUtilsBase();

View File

@ -23,8 +23,10 @@
#include <QSettings>
#include <QStandardPaths>
#include <ApplicationServices/ApplicationServices.h>
#include <CoreGraphics/CGEventSource.h>
#define INVALID_KEYCODE 0xFFFF
QPointer<MacUtils> MacUtils::m_instance = nullptr;
@ -95,9 +97,10 @@ bool MacUtils::isDarkMode() const
QString MacUtils::getLaunchAgentFilename() const
{
auto launchAgentDir = QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
return QFile(launchAgentDir.absoluteFilePath(
qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist"))).fileName();
auto launchAgentDir =
QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
return QFile(launchAgentDir.absoluteFilePath(qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist")))
.fileName();
}
bool MacUtils::isLaunchAtStartupEnabled() const
@ -134,3 +137,306 @@ void MacUtils::toggleForegroundApp(bool foreground)
{
m_appkit->toggleForegroundApp(foreground);
}
void MacUtils::registerNativeEventFilter()
{
EventTypeSpec eventSpec;
eventSpec.eventClass = kEventClassKeyboard;
eventSpec.eventKind = kEventHotKeyPressed;
::InstallApplicationEventHandler(MacUtils::hotkeyHandler, 1, &eventSpec, this, nullptr);
}
bool MacUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
{
auto keycode = qtToNativeKeyCode(key);
auto modifierscode = qtToNativeModifiers(modifiers, false);
if (keycode == INVALID_KEYCODE) {
if (error) {
*error = tr("Invalid key code");
}
return false;
}
// Check if this key combo is registered to another shortcut
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
if (error) {
*error = tr("Global shortcut already registered to %1").arg(i.key());
}
return false;
}
}
// Remove existing registration for this name
unregisterGlobalShortcut(name);
auto gs = QSharedPointer<globalShortcut>::create();
gs->hotkeyId.signature = 'kpxc';
gs->hotkeyId.id = m_nextShortcutId;
gs->nativeKeyCode = keycode;
gs->nativeModifiers = modifierscode;
if (::RegisterEventHotKey(
gs->nativeKeyCode, gs->nativeModifiers, gs->hotkeyId, GetApplicationEventTarget(), 0, &gs->hotkeyRef)
!= noErr) {
if (error) {
*error = tr("Could not register global shortcut");
}
return false;
}
m_globalShortcuts.insert(name, gs);
++m_nextShortcutId;
return true;
}
bool MacUtils::unregisterGlobalShortcut(const QString& name)
{
if (m_globalShortcuts.contains(name)) {
auto gs = m_globalShortcuts.value(name);
::UnregisterEventHotKey(gs->hotkeyRef);
m_globalShortcuts.remove(name);
return true;
}
return false;
}
OSStatus MacUtils::hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
{
Q_UNUSED(nextHandler);
auto self = static_cast<MacUtils*>(userData);
EventHotKeyID hotkeyId;
if (::GetEventParameter(
theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId)
== noErr) {
QHashIterator<QString, QSharedPointer<globalShortcut>> i(self->m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->hotkeyId.id == hotkeyId.id) {
emit self->globalShortcutTriggered(i.key());
return noErr;
}
}
}
return eventNotHandledErr;
}
//
// Translate qt key code to mac os key code
// see: HIToolbox/Events.h
//
uint16 MacUtils::qtToNativeKeyCode(Qt::Key key)
{
switch (key) {
case Qt::Key_A:
return kVK_ANSI_A;
case Qt::Key_B:
return kVK_ANSI_B;
case Qt::Key_C:
return kVK_ANSI_C;
case Qt::Key_D:
return kVK_ANSI_D;
case Qt::Key_E:
return kVK_ANSI_E;
case Qt::Key_F:
return kVK_ANSI_F;
case Qt::Key_G:
return kVK_ANSI_G;
case Qt::Key_H:
return kVK_ANSI_H;
case Qt::Key_I:
return kVK_ANSI_I;
case Qt::Key_J:
return kVK_ANSI_J;
case Qt::Key_K:
return kVK_ANSI_K;
case Qt::Key_L:
return kVK_ANSI_L;
case Qt::Key_M:
return kVK_ANSI_M;
case Qt::Key_N:
return kVK_ANSI_N;
case Qt::Key_O:
return kVK_ANSI_O;
case Qt::Key_P:
return kVK_ANSI_P;
case Qt::Key_Q:
return kVK_ANSI_Q;
case Qt::Key_R:
return kVK_ANSI_R;
case Qt::Key_S:
return kVK_ANSI_S;
case Qt::Key_T:
return kVK_ANSI_T;
case Qt::Key_U:
return kVK_ANSI_U;
case Qt::Key_V:
return kVK_ANSI_V;
case Qt::Key_W:
return kVK_ANSI_W;
case Qt::Key_X:
return kVK_ANSI_X;
case Qt::Key_Y:
return kVK_ANSI_Y;
case Qt::Key_Z:
return kVK_ANSI_Z;
case Qt::Key_0:
return kVK_ANSI_0;
case Qt::Key_1:
return kVK_ANSI_1;
case Qt::Key_2:
return kVK_ANSI_2;
case Qt::Key_3:
return kVK_ANSI_3;
case Qt::Key_4:
return kVK_ANSI_4;
case Qt::Key_5:
return kVK_ANSI_5;
case Qt::Key_6:
return kVK_ANSI_6;
case Qt::Key_7:
return kVK_ANSI_7;
case Qt::Key_8:
return kVK_ANSI_8;
case Qt::Key_9:
return kVK_ANSI_9;
case Qt::Key_Equal:
return kVK_ANSI_Equal;
case Qt::Key_Minus:
return kVK_ANSI_Minus;
case Qt::Key_BracketRight:
return kVK_ANSI_RightBracket;
case Qt::Key_BracketLeft:
return kVK_ANSI_LeftBracket;
case Qt::Key_QuoteDbl:
return kVK_ANSI_Quote;
case Qt::Key_Semicolon:
return kVK_ANSI_Semicolon;
case Qt::Key_Backslash:
return kVK_ANSI_Backslash;
case Qt::Key_Comma:
return kVK_ANSI_Comma;
case Qt::Key_Slash:
return kVK_ANSI_Slash;
case Qt::Key_Period:
return kVK_ANSI_Period;
case Qt::Key_Shift:
return kVK_Shift;
case Qt::Key_Control:
return kVK_Command;
case Qt::Key_Backspace:
return kVK_Delete;
case Qt::Key_Tab:
case Qt::Key_Backtab:
return kVK_Tab;
case Qt::Key_Enter:
case Qt::Key_Return:
return kVK_Return;
case Qt::Key_CapsLock:
return kVK_CapsLock;
case Qt::Key_Escape:
return kVK_Escape;
case Qt::Key_Space:
return kVK_Space;
case Qt::Key_PageUp:
return kVK_PageUp;
case Qt::Key_PageDown:
return kVK_PageDown;
case Qt::Key_End:
return kVK_End;
case Qt::Key_Home:
return kVK_Home;
case Qt::Key_Left:
return kVK_LeftArrow;
case Qt::Key_Up:
return kVK_UpArrow;
case Qt::Key_Right:
return kVK_RightArrow;
case Qt::Key_Down:
return kVK_DownArrow;
case Qt::Key_Delete:
return kVK_ForwardDelete;
case Qt::Key_Help:
return kVK_Help;
case Qt::Key_F1:
return kVK_F1;
case Qt::Key_F2:
return kVK_F2;
case Qt::Key_F3:
return kVK_F3;
case Qt::Key_F4:
return kVK_F4;
case Qt::Key_F5:
return kVK_F5;
case Qt::Key_F6:
return kVK_F6;
case Qt::Key_F7:
return kVK_F7;
case Qt::Key_F8:
return kVK_F8;
case Qt::Key_F9:
return kVK_F9;
case Qt::Key_F10:
return kVK_F10;
case Qt::Key_F11:
return kVK_F11;
case Qt::Key_F12:
return kVK_F12;
case Qt::Key_F13:
return kVK_F13;
case Qt::Key_F14:
return kVK_F14;
case Qt::Key_F15:
return kVK_F15;
case Qt::Key_F16:
return kVK_F16;
default:
Q_ASSERT(false);
return INVALID_KEYCODE;
}
}
//
// Translate qt key modifiers to mac os modifiers
// see: https://doc.qt.io/qt-5/osx-issues.html#special-keys
//
CGEventFlags MacUtils::qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native)
{
CGEventFlags nativeModifiers = CGEventFlags(0);
CGEventFlags shiftMod = CGEventFlags(shiftKey);
CGEventFlags cmdMod = CGEventFlags(cmdKey);
CGEventFlags optionMod = CGEventFlags(optionKey);
CGEventFlags controlMod = CGEventFlags(controlKey);
if (native) {
shiftMod = kCGEventFlagMaskShift;
cmdMod = kCGEventFlagMaskCommand;
optionMod = kCGEventFlagMaskAlternate;
controlMod = kCGEventFlagMaskControl;
}
if (modifiers & Qt::ShiftModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | shiftMod);
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | cmdMod);
}
if (modifiers & Qt::AltModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | optionMod);
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers = CGEventFlags(nativeModifiers | controlMod);
}
return nativeModifiers;
}

View File

@ -19,8 +19,9 @@
#ifndef KEEPASSXC_MACUTILS_H
#define KEEPASSXC_MACUTILS_H
#include "gui/osutils/OSUtilsBase.h"
#include "AppKit.h"
#include "gui/osutils/OSUtilsBase.h"
#include <Carbon/Carbon.h>
#include <QPointer>
#include <QScopedPointer>
@ -48,6 +49,17 @@ public:
bool enableScreenRecording();
void toggleForegroundApp(bool foreground);
void registerNativeEventFilter() override;
bool registerGlobalShortcut(const QString& name,
Qt::Key key,
Qt::KeyboardModifiers modifiers,
QString* error = nullptr) override;
bool unregisterGlobalShortcut(const QString& name) override;
uint16 qtToNativeKeyCode(Qt::Key key);
CGEventFlags qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native);
signals:
void lockDatabases();
@ -57,10 +69,22 @@ protected:
private:
QString getLaunchAgentFilename() const;
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
QScopedPointer<AppKit> m_appkit;
static QPointer<MacUtils> m_instance;
struct globalShortcut
{
EventHotKeyRef hotkeyRef;
EventHotKeyID hotkeyId;
uint16 nativeKeyCode;
CGEventFlags nativeModifiers;
};
int m_nextShortcutId = 1;
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
Q_DISABLE_COPY(MacUtils)
};

View File

@ -2,10 +2,10 @@
* Automatically generated by keysymmap.py from parsing keysymdef.h.
*/
const int AutoTypePlatformX11::m_unicodeToKeysymLen = 632;
const int unicodeToKeysymLen = 632;
// clang-format off
const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {
const uint unicodeToKeysymKeys[] = {
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
0x0108, 0x0109, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, 0x010f,
0x0110, 0x0111, 0x0112, 0x0113, 0x0116, 0x0117, 0x0118, 0x0119,
@ -87,7 +87,7 @@ const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {
0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f2, 0x30f3, 0x30fb, 0x30fc
};
const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {
const uint unicodeToKeysymValues[] = {
0x03c0, 0x03e0, 0x01c3, 0x01e3, 0x01a1, 0x01b1, 0x01c6, 0x01e6,
0x02c6, 0x02e6, 0x02c5, 0x02e5, 0x01c8, 0x01e8, 0x01cf, 0x01ef,
0x01d0, 0x01f0, 0x03aa, 0x03ba, 0x03cc, 0x03ec, 0x01ca, 0x01ea,

View File

@ -16,6 +16,8 @@
*/
#include "NixUtils.h"
#include "KeySymMap.h"
#include "core/Tools.h"
#include <QApplication>
#include <QColor>
@ -26,13 +28,26 @@
#include <QStandardPaths>
#include <QStyle>
#include <QTextStream>
#include <QtX11Extras/QX11Info>
#include <qpa/qplatformnativeinterface.h>
// namespace required to avoid name clashes with declarations in XKBlib.h
namespace X11
{
#include "X11Funcs.h"
#include <X11/XKBlib.h>
}
#include <xcb/xproto.h>
namespace
{
Display* dpy;
Window rootWindow;
bool x11ErrorOccurred = false;
int x11ErrorHandler(Display*, XErrorEvent*)
{
x11ErrorOccurred = true;
return 1;
}
} // namespace
QPointer<NixUtils> NixUtils::m_instance = nullptr;
@ -48,6 +63,8 @@ NixUtils* NixUtils::instance()
NixUtils::NixUtils(QObject* parent)
: OSUtilsBase(parent)
{
dpy = QX11Info::display();
rootWindow = QX11Info::appRootWindow();
}
NixUtils::~NixUtils()
@ -125,7 +142,7 @@ bool NixUtils::isCapslockEnabled()
QString platform = QGuiApplication::platformName();
if (platform == "xcb") {
unsigned state = 0;
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
if (XkbGetIndicatorState(reinterpret_cast<Display*>(display), XkbUseCoreKbd, &state) == Success) {
return ((state & 1u) != 0);
}
}
@ -134,3 +151,119 @@ bool NixUtils::isCapslockEnabled()
return false;
}
void NixUtils::registerNativeEventFilter()
{
qApp->installNativeEventFilter(this);
}
bool NixUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
{
if (eventType != QByteArrayLiteral("xcb_generic_event_t")) {
return false;
}
auto* genericEvent = static_cast<xcb_generic_event_t*>(message);
quint8 type = genericEvent->response_type & 0x7f;
if (type == XCB_KEY_PRESS) {
auto* keyPressEvent = static_cast<xcb_key_press_event_t*>(message);
auto modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
return triggerGlobalShortcut(keyPressEvent->detail, keyPressEvent->state & modifierMask);
} else if (type == XCB_MAPPING_NOTIFY) {
auto* mappingNotifyEvent = static_cast<xcb_mapping_notify_event_t*>(message);
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD
|| mappingNotifyEvent->request == XCB_MAPPING_MODIFIER) {
XMappingEvent xMappingEvent;
memset(&xMappingEvent, 0, sizeof(xMappingEvent));
xMappingEvent.type = MappingNotify;
xMappingEvent.display = dpy;
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD) {
xMappingEvent.request = MappingKeyboard;
} else {
xMappingEvent.request = MappingModifier;
}
xMappingEvent.first_keycode = mappingNotifyEvent->first_keycode;
xMappingEvent.count = mappingNotifyEvent->count;
XRefreshKeyboardMapping(&xMappingEvent);
// Notify listeners that the keymap has changed
emit keymapChanged();
}
}
return false;
}
bool NixUtils::triggerGlobalShortcut(uint keycode, uint modifiers)
{
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifiers) {
emit globalShortcutTriggered(i.key());
return true;
}
}
return false;
}
bool NixUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
{
auto keycode = XKeysymToKeycode(dpy, qcharToNativeKeyCode(key));
auto modifierscode = qtToNativeModifiers(modifiers);
// Check if this key combo is registered to another shortcut
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
if (error) {
*error = tr("Global shortcut already registered to %1").arg(i.key());
}
return false;
}
}
unregisterGlobalShortcut(name);
x11ErrorOccurred = false;
auto prevHandler = XSetErrorHandler(x11ErrorHandler);
XGrabKey(dpy, keycode, modifierscode, rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, keycode, modifierscode | Mod2Mask, rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, keycode, modifierscode | LockMask, rootWindow, True, GrabModeAsync, GrabModeAsync);
XGrabKey(dpy, keycode, modifierscode | Mod2Mask | LockMask, rootWindow, True, GrabModeAsync, GrabModeAsync);
XSync(dpy, False);
XSetErrorHandler(prevHandler);
if (x11ErrorOccurred) {
x11ErrorOccurred = false;
if (error) {
*error = tr("Could not register global shortcut");
}
return false;
}
auto gs = QSharedPointer<globalShortcut>::create();
gs->nativeKeyCode = keycode;
gs->nativeModifiers = modifierscode;
m_globalShortcuts.insert(name, gs);
return true;
}
bool NixUtils::unregisterGlobalShortcut(const QString& name)
{
if (!m_globalShortcuts.contains(name)) {
return false;
}
auto gs = m_globalShortcuts.value(name);
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers, rootWindow);
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | Mod2Mask, rootWindow);
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | LockMask, rootWindow);
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | Mod2Mask | LockMask, rootWindow);
m_globalShortcuts.remove(name);
return true;
}

View File

@ -19,9 +19,10 @@
#define KEEPASSXC_NIXUTILS_H
#include "gui/osutils/OSUtilsBase.h"
#include <QAbstractNativeEventFilter>
#include <QPointer>
class NixUtils : public OSUtilsBase
class NixUtils : public OSUtilsBase, QAbstractNativeEventFilter
{
Q_OBJECT
@ -33,15 +34,35 @@ public:
void setLaunchAtStartup(bool enable) override;
bool isCapslockEnabled() override;
void registerNativeEventFilter() override;
bool registerGlobalShortcut(const QString& name,
Qt::Key key,
Qt::KeyboardModifiers modifiers,
QString* error = nullptr) override;
bool unregisterGlobalShortcut(const QString& name) override;
signals:
void keymapChanged();
private:
explicit NixUtils(QObject* parent = nullptr);
~NixUtils() override;
private:
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
QString getAutostartDesktopFilename(bool createDirs = false) const;
bool triggerGlobalShortcut(uint keycode, uint modifiers);
static QPointer<NixUtils> m_instance;
struct globalShortcut
{
uint nativeKeyCode;
uint nativeModifiers;
};
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
Q_DISABLE_COPY(NixUtils)
};

View File

@ -0,0 +1,126 @@
/*
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "X11Funcs.h"
#include "KeySymMap.h"
#include "core/Tools.h"
#include <X11/Xutil.h>
KeySym qcharToNativeKeyCode(const QChar& ch)
{
ushort unicode = ch.unicode();
/* first check for Latin-1 characters (1:1 mapping) */
if ((unicode >= 0x0020 && unicode <= 0x007e) || (unicode >= 0x00a0 && unicode <= 0x00ff)) {
return unicode;
}
/* mapping table generated from keysymdef.h */
const uint* match = Tools::binaryFind(unicodeToKeysymKeys, unicodeToKeysymKeys + unicodeToKeysymLen, unicode);
int index = match - unicodeToKeysymKeys;
if (index != unicodeToKeysymLen) {
return unicodeToKeysymValues[index];
}
if (unicode >= 0x0100) {
return unicode | 0x01000000;
}
return NoSymbol;
}
KeySym qtToNativeKeyCode(Qt::Key key)
{
switch (key) {
case Qt::Key_Tab:
return XK_Tab;
case Qt::Key_Enter:
return XK_Return;
case Qt::Key_Space:
return XK_space;
case Qt::Key_Up:
return XK_Up;
case Qt::Key_Down:
return XK_Down;
case Qt::Key_Left:
return XK_Left;
case Qt::Key_Right:
return XK_Right;
case Qt::Key_Insert:
return XK_Insert;
case Qt::Key_Delete:
return XK_Delete;
case Qt::Key_Home:
return XK_Home;
case Qt::Key_End:
return XK_End;
case Qt::Key_PageUp:
return XK_Page_Up;
case Qt::Key_PageDown:
return XK_Page_Down;
case Qt::Key_Backspace:
return XK_BackSpace;
case Qt::Key_Pause:
return XK_Break;
case Qt::Key_CapsLock:
return XK_Caps_Lock;
case Qt::Key_Escape:
return XK_Escape;
case Qt::Key_Help:
return XK_Help;
case Qt::Key_NumLock:
return XK_Num_Lock;
case Qt::Key_Print:
return XK_Print;
case Qt::Key_ScrollLock:
return XK_Scroll_Lock;
case Qt::Key_Shift:
return XK_Shift_L;
case Qt::Key_Control:
return XK_Control_L;
case Qt::Key_Alt:
return XK_Alt_L;
default:
if (key >= Qt::Key_F1 && key <= Qt::Key_F16) {
return XK_F1 + (key - Qt::Key_F1);
} else {
return NoSymbol;
}
}
}
uint qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
{
uint nativeModifiers = 0;
if (modifiers & Qt::ShiftModifier) {
nativeModifiers |= ShiftMask;
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers |= ControlMask;
}
if (modifiers & Qt::AltModifier) {
nativeModifiers |= Mod1Mask;
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers |= Mod4Mask;
}
return nativeModifiers;
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSXC_X11FUNCS_H
#define KEEPASSXC_X11FUNCS_H
#include <QChar>
#include <qnamespace.h>
#include <X11/X.h>
KeySym qcharToNativeKeyCode(const QChar& ch);
KeySym qtToNativeKeyCode(Qt::Key key);
uint qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
#endif

View File

@ -62,11 +62,11 @@ print("""/*
*/
""")
print("const int AutoTypePlatformX11::m_unicodeToKeysymLen = {0};".format(len(keysymMap)))
print("const int unicodeToKeysymLen = {0};".format(len(keysymMap)))
print()
print("const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {")
print("const uint unicodeToKeysymKeys[] = {")
keys = keysymMap.keys()
keyLen = len(keys)
for idx, val in enumerate(keys, start=1):
@ -84,7 +84,7 @@ print("};")
print()
print("const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {")
print("const uint unicodeToKeysymValues[] = {")
values = keysymMap.values()
valuesLen = len(values)
for idx, val in enumerate(values, start=1):

View File

@ -24,7 +24,6 @@
#include <windows.h>
QPointer<WinUtils> WinUtils::m_instance = nullptr;
QScopedPointer<WinUtils::DWMEventFilter> WinUtils::m_eventFilter;
WinUtils* WinUtils::instance()
{
@ -48,39 +47,34 @@ WinUtils::WinUtils(QObject* parent)
{
}
WinUtils::~WinUtils()
{
}
/**
* Register event filters to handle native platform events such as theme changes.
* Register event filters to handle native platform events such as global hotkeys
*/
void WinUtils::registerEventFilters()
void WinUtils::registerNativeEventFilter()
{
if (!m_eventFilter) {
m_eventFilter.reset(new DWMEventFilter);
qApp->installNativeEventFilter(m_eventFilter.data());
}
qApp->installNativeEventFilter(this);
}
bool WinUtils::DWMEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long*)
bool WinUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
{
if (eventType != "windows_generic_MSG") {
return false;
}
auto* msg = static_cast<MSG*>(message);
if (!msg->hwnd) {
return false;
}
switch (msg->message) {
/* TODO: indicate dark mode support for black title bar
case WM_CREATE:
case WM_INITDIALOG: {
if (winUtils()->isDarkMode()) {
// TODO: indicate dark mode support for black title bar
if (msg->hwnd && winUtils()->isDarkMode()) {
}
break;
}
*/
case WM_HOTKEY:
triggerGlobalShortcut(msg->wParam);
break;
}
return false;
@ -119,3 +113,319 @@ bool WinUtils::isHighContrastMode() const
QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat);
return (settings.value("Flags").toInt() & 1u) != 0;
}
bool WinUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
{
auto keycode = qtToNativeKeyCode(key);
auto modifierscode = qtToNativeModifiers(modifiers);
if (keycode < 1 || keycode > 254) {
if (error) {
*error = tr("Invalid key code");
}
return false;
}
// Check if this key combo is registered to another shortcut
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
if (error) {
*error = tr("Global shortcut already registered to %1").arg(i.key());
}
return false;
}
}
unregisterGlobalShortcut(name);
auto gs = QSharedPointer<globalShortcut>::create();
gs->id = m_nextShortcutId;
gs->nativeKeyCode = keycode;
gs->nativeModifiers = modifierscode;
if (!::RegisterHotKey(nullptr, gs->id, gs->nativeModifiers | MOD_NOREPEAT, gs->nativeKeyCode)) {
if (error) {
*error = tr("Could not register global shortcut");
}
return false;
}
m_globalShortcuts.insert(name, gs);
if (++m_nextShortcutId > 0xBFFF) {
// Roll over if greater than the max id per
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey#remarks
m_nextShortcutId = 1;
}
return true;
}
bool WinUtils::unregisterGlobalShortcut(const QString& name)
{
if (m_globalShortcuts.contains(name)) {
auto gs = m_globalShortcuts.value(name);
if (::UnregisterHotKey(nullptr, gs->id)) {
m_globalShortcuts.remove(name);
return true;
}
}
return false;
}
void WinUtils::triggerGlobalShortcut(int id)
{
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
while (i.hasNext()) {
i.next();
if (i.value()->id == id) {
emit globalShortcutTriggered(i.key());
break;
}
}
}
// clang-format off
//
// Translate qt key code to windows virtual key code
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
//
DWORD WinUtils::qtToNativeKeyCode(Qt::Key key)
{
switch (key) {
case Qt::Key_Backspace:
return VK_BACK; // 0x08
case Qt::Key_Tab:
case Qt::Key_Backtab:
return VK_TAB; // 0x09
case Qt::Key_Clear:
return VK_CLEAR; // 0x0C
case Qt::Key_Enter:
case Qt::Key_Return:
return VK_RETURN; // 0x0D
case Qt::Key_Shift:
return VK_SHIFT; // 0x10
case Qt::Key_Control:
return VK_CONTROL; // 0x11
case Qt::Key_Pause:
return VK_PAUSE; // 0x13
case Qt::Key_CapsLock:
return VK_CAPITAL; // 0x14
case Qt::Key_Escape:
return VK_ESCAPE; // 0x1B
case Qt::Key_Space:
return VK_SPACE; // 0x20
case Qt::Key_PageUp:
return VK_PRIOR; // 0x21
case Qt::Key_PageDown:
return VK_NEXT; // 0x22
case Qt::Key_End:
return VK_END; // 0x23
case Qt::Key_Home:
return VK_HOME; // 0x24
case Qt::Key_Left:
return VK_LEFT; // 0x25
case Qt::Key_Up:
return VK_UP; // 0x26
case Qt::Key_Right:
return VK_RIGHT; // 0x27
case Qt::Key_Down:
return VK_DOWN; // 0x28
case Qt::Key_Print:
return VK_SNAPSHOT; // 0x2C
case Qt::Key_Insert:
return VK_INSERT; // 0x2D
case Qt::Key_Delete:
return VK_DELETE; // 0x2E
case Qt::Key_Help:
return VK_HELP; // 0x2F
case Qt::Key_0:
return 0x30; // 0x30
case Qt::Key_1:
return 0x31; // 0x31
case Qt::Key_2:
return 0x32; // 0x32
case Qt::Key_3:
return 0x33; // 0x33
case Qt::Key_4:
return 0x34; // 0x34
case Qt::Key_5:
return 0x35; // 0x35
case Qt::Key_6:
return 0x36; // 0x36
case Qt::Key_7:
return 0x37; // 0x37
case Qt::Key_8:
return 0x38; // 0x38
case Qt::Key_9:
return 0x39; // 0x39
case Qt::Key_A:
return 0x41; // 0x41
case Qt::Key_B:
return 0x42; // 0x42
case Qt::Key_C:
return 0x43; // 0x43
case Qt::Key_D:
return 0x44; // 0x44
case Qt::Key_E:
return 0x45; // 0x45
case Qt::Key_F:
return 0x46; // 0x46
case Qt::Key_G:
return 0x47; // 0x47
case Qt::Key_H:
return 0x48; // 0x48
case Qt::Key_I:
return 0x49; // 0x49
case Qt::Key_J:
return 0x4A; // 0x4A
case Qt::Key_K:
return 0x4B; // 0x4B
case Qt::Key_L:
return 0x4C; // 0x4C
case Qt::Key_M:
return 0x4D; // 0x4D
case Qt::Key_N:
return 0x4E; // 0x4E
case Qt::Key_O:
return 0x4F; // 0x4F
case Qt::Key_P:
return 0x50; // 0x50
case Qt::Key_Q:
return 0x51; // 0x51
case Qt::Key_R:
return 0x52; // 0x52
case Qt::Key_S:
return 0x53; // 0x53
case Qt::Key_T:
return 0x54; // 0x54
case Qt::Key_U:
return 0x55; // 0x55
case Qt::Key_V:
return 0x56; // 0x56
case Qt::Key_W:
return 0x57; // 0x57
case Qt::Key_X:
return 0x58; // 0x58
case Qt::Key_Y:
return 0x59; // 0x59
case Qt::Key_Z:
return 0x5A; // 0x5A
case Qt::Key_F1:
return VK_F1; // 0x70
case Qt::Key_F2:
return VK_F2; // 0x71
case Qt::Key_F3:
return VK_F3; // 0x72
case Qt::Key_F4:
return VK_F4; // 0x73
case Qt::Key_F5:
return VK_F5; // 0x74
case Qt::Key_F6:
return VK_F6; // 0x75
case Qt::Key_F7:
return VK_F7; // 0x76
case Qt::Key_F8:
return VK_F8; // 0x77
case Qt::Key_F9:
return VK_F9; // 0x78
case Qt::Key_F10:
return VK_F10; // 0x79
case Qt::Key_F11:
return VK_F11; // 0x7A
case Qt::Key_F12:
return VK_F12; // 0x7B
case Qt::Key_F13:
return VK_F13; // 0x7C
case Qt::Key_F14:
return VK_F14; // 0x7D
case Qt::Key_F15:
return VK_F15; // 0x7E
case Qt::Key_F16:
return VK_F16; // 0x7F
case Qt::Key_F17:
return VK_F17; // 0x80
case Qt::Key_F18:
return VK_F18; // 0x81
case Qt::Key_F19:
return VK_F19; // 0x82
case Qt::Key_F20:
return VK_F20; // 0x83
case Qt::Key_F21:
return VK_F21; // 0x84
case Qt::Key_F22:
return VK_F22; // 0x85
case Qt::Key_F23:
return VK_F23; // 0x86
case Qt::Key_F24:
return VK_F24; // 0x87
case Qt::Key_NumLock:
return VK_NUMLOCK; // 0x90
case Qt::Key_ScrollLock:
return VK_SCROLL; // 0x91
case Qt::Key_Exclam: // !
case Qt::Key_QuoteDbl: // "
case Qt::Key_NumberSign: // #
case Qt::Key_Dollar: // $
case Qt::Key_Percent: // %
case Qt::Key_Ampersand: // &
case Qt::Key_Apostrophe: // '
case Qt::Key_ParenLeft: // (
case Qt::Key_ParenRight: // )
case Qt::Key_Asterisk: // *
case Qt::Key_Plus: // +
case Qt::Key_Comma: // ,
case Qt::Key_Minus: // -
case Qt::Key_Period: // .
case Qt::Key_Slash: // /
case Qt::Key_Colon: // :
case Qt::Key_Semicolon: // ;
case Qt::Key_Less: // <
case Qt::Key_Equal: // =
case Qt::Key_Greater: // >
case Qt::Key_Question: // ?
case Qt::Key_BracketLeft: // [
case Qt::Key_Backslash: // '\'
case Qt::Key_BracketRight: // ]
case Qt::Key_AsciiCircum: // ^
case Qt::Key_Underscore: // _
case Qt::Key_QuoteLeft: // `
case Qt::Key_BraceLeft: // {
case Qt::Key_Bar: // |
case Qt::Key_BraceRight: // }
case Qt::Key_AsciiTilde: // ~
return LOBYTE(::VkKeyScanExW(key, ::GetKeyboardLayout(0)));
default:
Q_ASSERT(false);
return 0;
}
}
// clang-format on
//
// Translate qt key modifiers to windows modifiers
//
DWORD WinUtils::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
{
DWORD nativeModifiers = 0;
if (modifiers & Qt::ShiftModifier) {
nativeModifiers |= MOD_SHIFT;
}
if (modifiers & Qt::ControlModifier) {
nativeModifiers |= MOD_CONTROL;
}
if (modifiers & Qt::AltModifier) {
nativeModifiers |= MOD_ALT;
}
if (modifiers & Qt::MetaModifier) {
nativeModifiers |= MOD_WIN;
}
return nativeModifiers;
}

View File

@ -23,14 +23,16 @@
#include <QAbstractNativeEventFilter>
#include <QPointer>
#include <QScopedPointer>
#include <QSharedPointer>
class WinUtils : public OSUtilsBase
#include <windef.h>
class WinUtils : public OSUtilsBase, QAbstractNativeEventFilter
{
Q_OBJECT
public:
static WinUtils* instance();
static void registerEventFilters();
bool isDarkMode() const override;
bool isLaunchAtStartupEnabled() const override;
@ -38,19 +40,36 @@ public:
bool isCapslockEnabled() override;
bool isHighContrastMode() const;
void registerNativeEventFilter() override;
bool registerGlobalShortcut(const QString& name,
Qt::Key key,
Qt::KeyboardModifiers modifiers,
QString* error = nullptr) override;
bool unregisterGlobalShortcut(const QString& name) override;
DWORD qtToNativeKeyCode(Qt::Key key);
DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
protected:
explicit WinUtils(QObject* parent = nullptr);
~WinUtils() override;
~WinUtils() override = default;
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
void triggerGlobalShortcut(int id);
private:
class DWMEventFilter : public QAbstractNativeEventFilter
static QPointer<WinUtils> m_instance;
struct globalShortcut
{
public:
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
int id;
DWORD nativeKeyCode;
DWORD nativeModifiers;
};
static QPointer<WinUtils> m_instance;
static QScopedPointer<DWMEventFilter> m_eventFilter;
int m_nextShortcutId = 1;
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
Q_DISABLE_COPY(WinUtils)
};

View File

@ -119,8 +119,6 @@ void DarkStyle::polish(QWidget* widget)
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x252525));
}
#elif defined(Q_OS_WIN)
// Register event filter for better dark mode support
WinUtils::registerEventFilters();
palette.setColor(QPalette::All, QPalette::Window, QRgb(0x2F2F30));
#else
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x2F2F30));

View File

@ -28,6 +28,7 @@
#include "core/Resources.h"
#include "crypto/Crypto.h"
#include "gui/MessageBox.h"
#include "gui/osutils/OSUtils.h"
QTEST_GUILESS_MAIN(TestAutoType)
@ -157,7 +158,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch()
void TestAutoType::testGlobalAutoTypeWithOneMatch()
{
m_test->setActiveWindowTitle("custom window");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
@ -168,7 +169,7 @@ void TestAutoType::testGlobalAutoTypeTitleMatch()
config()->set(Config::AutoTypeEntryTitleMatch, true);
m_test->setActiveWindowTitle("An Entry Title!");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
@ -179,7 +180,7 @@ void TestAutoType::testGlobalAutoTypeUrlMatch()
config()->set(Config::AutoTypeEntryTitleMatch, true);
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
@ -190,7 +191,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
config()->set(Config::AutoTypeEntryTitleMatch, true);
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
@ -199,7 +200,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
{
m_test->setActiveWindowTitle("An Entry Title!");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
MessageBox::setNextAnswer(MessageBox::Ok);
m_autoType->performGlobalAutoType(m_dbList);
@ -210,68 +211,68 @@ void TestAutoType::testGlobalAutoTypeRegExp()
{
// substring matches are ok
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex1"));
m_test->clearActions();
// should be case-insensitive
m_test->setActiveWindowTitle("lorem regex1 ipsum");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex1"));
m_test->clearActions();
// exact match
m_test->setActiveWindowTitle("REGEX2");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex2"));
m_test->clearActions();
// a bit more complicated regex
m_test->setActiveWindowTitle("REGEX3-R2D2");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("regex3"));
m_test->clearActions();
// with custom attributes
m_test->setActiveWindowTitle("CustomAttr1");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
m_test->clearActions();
// with (non uppercase) undefined custom attributes
m_test->setActiveWindowTitle("CustomAttr2");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString(""));
m_test->clearActions();
// with mixedcase default attributes
m_test->setActiveWindowTitle("CustomAttr3");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
m_test->clearActions();
// with resolve placeholders in window association title
m_test->setActiveWindowTitle("AttrValueFirst");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
m_test->clearActions();
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
m_test->clearActions();
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
m_test->triggerGlobalAutoType();
emit osUtils->globalShortcutTriggered("autotype");
m_autoType->performGlobalAutoType(m_dbList);
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
m_test->clearActions();

View File

@ -70,13 +70,13 @@ void TestCli::initTestCase()
Config::createTempFileInstance();
Bootstrap::bootstrap();
auto fd = new QFile();
m_devNull.reset(new QFile());
#ifdef Q_OS_WIN
fd->open(fopen("nul", "w"), QIODevice::WriteOnly);
m_devNull->open(fopen("nul", "w"), QIODevice::WriteOnly);
#else
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
m_devNull->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
#endif
Utils::DEVNULL.setDevice(fd);
Utils::DEVNULL.setDevice(m_devNull.data());
}
void TestCli::init()
@ -131,6 +131,11 @@ void TestCli::cleanup()
Utils::STDIN.setDevice(nullptr);
}
void TestCli::cleanupTestCase()
{
m_devNull.reset();
}
QSharedPointer<Database> TestCli::readDatabase(const QString& filename, const QString& pw, const QString& keyfile)
{
auto db = QSharedPointer<Database>::create();
@ -520,8 +525,7 @@ void TestCli::testClip()
setInput("a");
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "-a", "TESTAttribute1", "/Sample Entry"});
QVERIFY(m_stderr->readAll().contains(
"ERROR: attribute TESTAttribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
QVERIFY(m_stderr->readAll().contains("ERROR: attribute TESTAttribute1 is ambiguous"));
setInput("a");
execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry"});
@ -1776,8 +1780,7 @@ void TestCli::testShow()
setInput("a");
execCmd(showCmd, {"show", m_dbFile->fileName(), "-a", "Testattribute1", "/Sample Entry"});
QCOMPARE(m_stdout->readAll(), QByteArray());
QVERIFY(m_stderr->readAll().contains(
"ERROR: attribute Testattribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
QVERIFY(m_stderr->readAll().contains("ERROR: attribute Testattribute1 is ambiguous"));
}
void TestCli::testInvalidDbFiles()

View File

@ -45,6 +45,7 @@ private slots:
void initTestCase();
void init();
void cleanup();
void cleanupTestCase();
void testBatchCommands();
void testAdd();
@ -81,6 +82,7 @@ private slots:
void testYubiKeyOption();
private:
QScopedPointer<QFile> m_devNull;
QScopedPointer<TemporaryFile> m_dbFile;
QScopedPointer<TemporaryFile> m_dbFile2;
QScopedPointer<TemporaryFile> m_dbFileMulti;