mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-06-19 12:14:37 -04:00
Move global shortcut handling into OSUtils (#5566)
Move global shortcut handling into OSUtils
This commit is contained in:
parent
a6f01349e8
commit
404fd941e8
35 changed files with 1124 additions and 1136 deletions
|
@ -305,11 +305,11 @@ if(WITH_COVERAGE AND CMAKE_COMPILER_IS_CLANGXX)
|
||||||
# `find src -iname '*.h' -or -iname '*.cpp'`
|
# `find src -iname '*.h' -or -iname '*.cpp'`
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(UNIX AND NOT APPLE)
|
||||||
check_add_gcc_compiler_flag("-Qunused-arguments")
|
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,--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,--no-add-needed -Wl,--as-needed")
|
||||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
|
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
|
||||||
endif()
|
endif()
|
||||||
|
@ -399,7 +399,7 @@ include(CLangFormat)
|
||||||
|
|
||||||
set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools)
|
set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools)
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED)
|
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus X11Extras REQUIRED)
|
||||||
elseif(APPLE)
|
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 ${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)
|
find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/opt/qt/lib/cmake /usr/local/Cellar/qt/*/lib/cmake ENV PATH)
|
||||||
|
|
|
@ -206,7 +206,8 @@ if(UNIX AND NOT APPLE)
|
||||||
${keepassx_SOURCES}
|
${keepassx_SOURCES}
|
||||||
gui/MainWindowAdaptor.cpp
|
gui/MainWindowAdaptor.cpp
|
||||||
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
|
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
|
||||||
gui/osutils/nixutils/NixUtils.cpp)
|
gui/osutils/nixutils/NixUtils.cpp
|
||||||
|
gui/osutils/nixutils/X11Funcs.cpp)
|
||||||
endif()
|
endif()
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
set(keepassx_SOURCES
|
set(keepassx_SOURCES
|
||||||
|
@ -214,11 +215,6 @@ if(MINGW)
|
||||||
gui/osutils/winutils/ScreenLockListenerWin.cpp
|
gui/osutils/winutils/ScreenLockListenerWin.cpp
|
||||||
gui/osutils/winutils/WinUtils.cpp)
|
gui/osutils/winutils/WinUtils.cpp)
|
||||||
endif()
|
endif()
|
||||||
if(MINGW OR (UNIX AND NOT APPLE))
|
|
||||||
set(keepassx_SOURCES
|
|
||||||
${keepassx_SOURCES}
|
|
||||||
gui/osutils/OSEventFilter.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(keepassx_SOURCES ${keepassx_SOURCES}
|
set(keepassx_SOURCES ${keepassx_SOURCES}
|
||||||
../share/icons/icons.qrc
|
../share/icons/icons.qrc
|
||||||
|
@ -343,7 +339,7 @@ if(WITH_XC_KEESHARE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
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)
|
if(Qt5MacExtras_FOUND)
|
||||||
target_link_libraries(keepassx_core Qt5::MacExtras)
|
target_link_libraries(keepassx_core Qt5::MacExtras)
|
||||||
endif()
|
endif()
|
||||||
|
@ -356,7 +352,7 @@ if(HAIKU)
|
||||||
target_link_libraries(keepassx_core network)
|
target_link_libraries(keepassx_core network)
|
||||||
endif()
|
endif()
|
||||||
if(UNIX AND NOT APPLE)
|
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})
|
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
if(MINGW)
|
if(MINGW)
|
||||||
|
|
|
@ -37,18 +37,13 @@
|
||||||
#include "core/Tools.h"
|
#include "core/Tools.h"
|
||||||
#include "gui/MainWindow.h"
|
#include "gui/MainWindow.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
|
#include "gui/osutils/OSUtils.h"
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
#include "gui/osutils/macutils/MacUtils.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AutoType* AutoType::m_instance = nullptr;
|
AutoType* AutoType::m_instance = nullptr;
|
||||||
|
|
||||||
AutoType::AutoType(QObject* parent, bool test)
|
AutoType::AutoType(QObject* parent, bool test)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_autoTypeDelay(0)
|
, m_autoTypeDelay(0)
|
||||||
, m_currentGlobalKey(static_cast<Qt::Key>(0))
|
|
||||||
, m_currentGlobalModifiers(nullptr)
|
|
||||||
, m_pluginLoader(new QPluginLoader(this))
|
, m_pluginLoader(new QPluginLoader(this))
|
||||||
, m_plugin(nullptr)
|
, m_plugin(nullptr)
|
||||||
, m_executor(nullptr)
|
, m_executor(nullptr)
|
||||||
|
@ -96,7 +91,11 @@ void AutoType::loadPlugin(const QString& pluginPath)
|
||||||
if (m_plugin) {
|
if (m_plugin) {
|
||||||
if (m_plugin->isAvailable()) {
|
if (m_plugin->isAvailable()) {
|
||||||
m_executor = m_plugin->createExecutor();
|
m_executor = m_plugin->createExecutor();
|
||||||
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType()));
|
connect(osUtils, &OSUtilsBase::globalShortcutTriggered, this, [this](QString name) {
|
||||||
|
if (name == "autotype") {
|
||||||
|
startGlobalAutoType();
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
unloadPlugin();
|
unloadPlugin();
|
||||||
}
|
}
|
||||||
|
@ -153,44 +152,18 @@ void AutoType::raiseWindow()
|
||||||
#endif
|
#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) {
|
if (!m_plugin) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != m_currentGlobalKey || modifiers != m_currentGlobalModifiers) {
|
return osUtils->registerGlobalShortcut("autotype", key, modifiers, error);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoType::unregisterGlobalShortcut()
|
void AutoType::unregisterGlobalShortcut()
|
||||||
{
|
{
|
||||||
if (m_plugin && m_currentGlobalKey && m_currentGlobalModifiers) {
|
osUtils->unregisterGlobalShortcut("autotype");
|
||||||
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int AutoType::callEventFilter(void* event)
|
|
||||||
{
|
|
||||||
if (!m_plugin) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_plugin->platformEventFilter(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -303,6 +276,23 @@ void AutoType::startGlobalAutoType()
|
||||||
m_windowForGlobal = m_plugin->activeWindow();
|
m_windowForGlobal = m_plugin->activeWindow();
|
||||||
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
|
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
|
||||||
#ifdef Q_OS_MACOS
|
#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;
|
m_windowState = WindowState::Normal;
|
||||||
if (getMainWindow()) {
|
if (getMainWindow()) {
|
||||||
if (getMainWindow()->isMinimized()) {
|
if (getMainWindow()->isMinimized()) {
|
||||||
|
|
|
@ -39,9 +39,8 @@ class AutoType : public QObject
|
||||||
|
|
||||||
public:
|
public:
|
||||||
QStringList windowTitles();
|
QStringList windowTitles();
|
||||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
|
||||||
void unregisterGlobalShortcut();
|
void unregisterGlobalShortcut();
|
||||||
int callEventFilter(void* event);
|
|
||||||
static bool checkSyntax(const QString& string);
|
static bool checkSyntax(const QString& string);
|
||||||
static bool checkHighRepetition(const QString& string);
|
static bool checkHighRepetition(const QString& string);
|
||||||
static bool checkSlowKeypress(const QString& string);
|
static bool checkSlowKeypress(const QString& string);
|
||||||
|
@ -99,8 +98,6 @@ private:
|
||||||
QMutex m_inAutoType;
|
QMutex m_inAutoType;
|
||||||
QMutex m_inGlobalAutoTypeDialog;
|
QMutex m_inGlobalAutoTypeDialog;
|
||||||
int m_autoTypeDelay;
|
int m_autoTypeDelay;
|
||||||
Qt::Key m_currentGlobalKey;
|
|
||||||
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
|
||||||
QPluginLoader* m_pluginLoader;
|
QPluginLoader* m_pluginLoader;
|
||||||
AutoTypePlatformInterface* m_plugin;
|
AutoTypePlatformInterface* m_plugin;
|
||||||
AutoTypeExecutor* m_executor;
|
AutoTypeExecutor* m_executor;
|
||||||
|
|
|
@ -32,9 +32,6 @@ public:
|
||||||
virtual QStringList windowTitles() = 0;
|
virtual QStringList windowTitles() = 0;
|
||||||
virtual WId activeWindow() = 0;
|
virtual WId activeWindow() = 0;
|
||||||
virtual QString activeWindowTitle() = 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 bool raiseWindow(WId window) = 0;
|
||||||
virtual void unload()
|
virtual void unload()
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "ShortcutWidget.h"
|
#include "ShortcutWidget.h"
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
#include <QToolTip>
|
||||||
|
|
||||||
#include "autotype/AutoType.h"
|
#include "autotype/AutoType.h"
|
||||||
|
|
||||||
|
@ -48,9 +49,11 @@ void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
|
||||||
displayShortcut(m_key, m_modifiers);
|
displayShortcut(m_key, m_modifiers);
|
||||||
|
|
||||||
if (autoType()->registerGlobalShortcut(m_key, m_modifiers)) {
|
QString error;
|
||||||
|
if (autoType()->registerGlobalShortcut(m_key, m_modifiers, &error)) {
|
||||||
setStyleSheet("");
|
setStyleSheet("");
|
||||||
} else {
|
} else {
|
||||||
|
QToolTip::showText(mapToGlobal(rect().bottomLeft()), error);
|
||||||
setStyleSheet("background-color: #FF9696;");
|
setStyleSheet("background-color: #FF9696;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,24 +22,12 @@
|
||||||
|
|
||||||
#include <ApplicationServices/ApplicationServices.h>
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
|
||||||
#define HOTKEY_ID 1
|
|
||||||
#define MAX_WINDOW_TITLE_LENGTH 1024
|
#define MAX_WINDOW_TITLE_LENGTH 1024
|
||||||
#define INVALID_KEYCODE 0xFFFF
|
#define INVALID_KEYCODE 0xFFFF
|
||||||
|
|
||||||
namespace {
|
|
||||||
bool accessibilityChecked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoTypePlatformMac::AutoTypePlatformMac()
|
AutoTypePlatformMac::AutoTypePlatformMac()
|
||||||
: m_hotkeyRef(nullptr)
|
|
||||||
, m_hotkeyId({ 'kpx2', HOTKEY_ID })
|
|
||||||
{
|
{
|
||||||
EventTypeSpec eventSpec;
|
|
||||||
eventSpec.eventClass = kEventClassKeyboard;
|
|
||||||
eventSpec.eventKind = kEventHotKeyPressed;
|
|
||||||
|
|
||||||
MessageBox::initializeButtonDefs();
|
MessageBox::initializeButtonDefs();
|
||||||
::InstallApplicationEventHandler(AutoTypePlatformMac::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -120,44 +108,6 @@ QString AutoTypePlatformMac::activeWindowTitle()
|
||||||
return title;
|
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()
|
AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
|
||||||
{
|
{
|
||||||
return new AutoTypeExecutorMac(this);
|
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)
|
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) {
|
if (keyCode == INVALID_KEYCODE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, keyCode, isKeyDown);
|
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, keyCode, isKeyDown);
|
||||||
CGEventFlags nativeModifiers = qtToNativeModifiers(modifiers, true);
|
CGEventFlags nativeModifiers = macUtils()->qtToNativeModifiers(modifiers, true);
|
||||||
if (keyEvent != nullptr) {
|
if (keyEvent != nullptr) {
|
||||||
::CGEventSetFlags(keyEvent, nativeModifiers);
|
::CGEventSetFlags(keyEvent, nativeModifiers);
|
||||||
::CGEventPost(kCGSessionEventTap, keyEvent);
|
::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
|
// Get window layer/level
|
||||||
//
|
//
|
||||||
|
@ -472,39 +205,6 @@ QString AutoTypePlatformMac::windowTitle(CFDictionaryRef window)
|
||||||
return title;
|
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 ------------------------------
|
// ------------------------------ AutoTypeExecutorMac ------------------------------
|
||||||
//
|
//
|
||||||
|
|
|
@ -38,9 +38,6 @@ public:
|
||||||
QStringList windowTitles() override;
|
QStringList windowTitles() override;
|
||||||
WId activeWindow() override;
|
WId activeWindow() override;
|
||||||
QString activeWindowTitle() 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;
|
bool raiseWindow(WId pid) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
|
@ -50,18 +47,9 @@ public:
|
||||||
void sendChar(const QChar& ch, bool isKeyDown);
|
void sendChar(const QChar& ch, bool isKeyDown);
|
||||||
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers);
|
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers);
|
||||||
|
|
||||||
signals:
|
|
||||||
void globalShortcutTriggered();
|
|
||||||
|
|
||||||
private:
|
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 int windowLayer(CFDictionaryRef window);
|
||||||
static QString windowTitle(CFDictionaryRef window);
|
static QString windowTitle(CFDictionaryRef window);
|
||||||
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoTypeExecutorMac : public AutoTypeExecutor
|
class AutoTypeExecutorMac : public AutoTypeExecutor
|
||||||
|
|
|
@ -42,37 +42,11 @@ QString AutoTypePlatformTest::activeWindowTitle()
|
||||||
return m_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()
|
AutoTypeExecutor* AutoTypePlatformTest::createExecutor()
|
||||||
{
|
{
|
||||||
return new AutoTypeExecutorTest(this);
|
return new AutoTypeExecutorTest(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypePlatformTest::triggerGlobalAutoType()
|
|
||||||
{
|
|
||||||
emit globalShortcutTriggered();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
|
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
|
||||||
{
|
{
|
||||||
m_activeWindowTitle = title;
|
m_activeWindowTitle = title;
|
||||||
|
|
|
@ -37,9 +37,6 @@ public:
|
||||||
QStringList windowTitles() override;
|
QStringList windowTitles() override;
|
||||||
WId activeWindow() override;
|
WId activeWindow() override;
|
||||||
QString activeWindowTitle() 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;
|
bool raiseWindow(WId window) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
|
@ -48,7 +45,6 @@ public:
|
||||||
bool raiseOwnWindow() override;
|
bool raiseOwnWindow() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void triggerGlobalAutoType() override;
|
|
||||||
void setActiveWindowTitle(const QString& title) override;
|
void setActiveWindowTitle(const QString& title) override;
|
||||||
|
|
||||||
QString actionChars() override;
|
QString actionChars() override;
|
||||||
|
@ -58,9 +54,6 @@ public:
|
||||||
void addActionChar(AutoTypeChar* action);
|
void addActionChar(AutoTypeChar* action);
|
||||||
void addActionKey(AutoTypeKey* action);
|
void addActionKey(AutoTypeKey* action);
|
||||||
|
|
||||||
signals:
|
|
||||||
void globalShortcutTriggered();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_activeWindowTitle;
|
QString m_activeWindowTitle;
|
||||||
QList<AutoTypeAction*> m_actionList;
|
QList<AutoTypeAction*> m_actionList;
|
||||||
|
|
|
@ -26,7 +26,6 @@ public:
|
||||||
virtual ~AutoTypeTestInterface()
|
virtual ~AutoTypeTestInterface()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
virtual void triggerGlobalAutoType() = 0;
|
|
||||||
virtual void setActiveWindowTitle(const QString& title) = 0;
|
virtual void setActiveWindowTitle(const QString& title) = 0;
|
||||||
|
|
||||||
virtual QString actionChars() = 0;
|
virtual QString actionChars() = 0;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AutoTypeWindows.h"
|
#include "AutoTypeWindows.h"
|
||||||
|
#include "gui/osutils/OSUtils.h"
|
||||||
|
|
||||||
#include <VersionHelpers.h>
|
#include <VersionHelpers.h>
|
||||||
|
|
||||||
|
@ -61,49 +62,6 @@ QString AutoTypePlatformWin::activeWindowTitle()
|
||||||
return windowTitle(::GetForegroundWindow());
|
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()
|
AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
|
||||||
{
|
{
|
||||||
return new AutoTypeExecutorWin(this);
|
return new AutoTypeExecutorWin(this);
|
||||||
|
@ -145,7 +103,7 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
||||||
//
|
//
|
||||||
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||||
{
|
{
|
||||||
DWORD nativeKeyCode = qtToNativeKeyCode(key);
|
DWORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
|
||||||
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -168,234 +126,12 @@ void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||||
::SendInput(1, &in, sizeof(INPUT));
|
::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
|
// The extended-key flag indicates whether the keystroke message originated
|
||||||
// from one of the additional keys on the enhanced keyboard
|
// 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
|
// 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) {
|
switch (nativeKeyCode) {
|
||||||
case VK_RMENU:
|
case VK_RMENU:
|
||||||
|
@ -417,44 +153,21 @@ BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
|
||||||
case VK_LWIN:
|
case VK_LWIN:
|
||||||
case VK_RWIN:
|
case VK_RWIN:
|
||||||
case VK_APPS:
|
case VK_APPS:
|
||||||
return TRUE;
|
return true;
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// clang-format on
|
// 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
|
// Test if window is in Alt+Tab list
|
||||||
// see: https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863
|
// see: https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863
|
||||||
//
|
//
|
||||||
BOOL AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
|
bool AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
|
||||||
{
|
{
|
||||||
if (!::IsWindowVisible(hwnd)) {
|
if (!::IsWindowVisible(hwnd)) {
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start at the root owner
|
// Start at the root owner
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#define KEEPASSX_AUTOTYPEWINDOWS_H
|
#define KEEPASSX_AUTOTYPEWINDOWS_H
|
||||||
|
|
||||||
#include <QtPlugin>
|
#include <QtPlugin>
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "autotype/AutoTypeAction.h"
|
#include "autotype/AutoTypeAction.h"
|
||||||
#include "autotype/AutoTypePlatformPlugin.h"
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
|
@ -36,23 +36,15 @@ public:
|
||||||
QStringList windowTitles() override;
|
QStringList windowTitles() override;
|
||||||
WId activeWindow() override;
|
WId activeWindow() override;
|
||||||
QString activeWindowTitle() 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;
|
bool raiseWindow(WId window) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
void sendChar(const QChar& ch, bool isKeyDown);
|
void sendChar(const QChar& ch, bool isKeyDown);
|
||||||
void sendKey(Qt::Key key, bool isKeyDown);
|
void sendKey(Qt::Key key, bool isKeyDown);
|
||||||
|
|
||||||
signals:
|
|
||||||
void globalShortcutTriggered();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static DWORD qtToNativeKeyCode(Qt::Key key);
|
static bool isExtendedKey(DWORD nativeKeyCode);
|
||||||
static DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
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 BOOL CALLBACK windowTitleEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam);
|
||||||
static QString windowTitle(HWND hwnd);
|
static QString windowTitle(HWND hwnd);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,15 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "AutoTypeXCB.h"
|
#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()
|
AutoTypePlatformX11::AutoTypePlatformX11()
|
||||||
{
|
{
|
||||||
|
@ -49,17 +40,14 @@ AutoTypePlatformX11::AutoTypePlatformX11()
|
||||||
m_classBlacklist << "xfdesktop"
|
m_classBlacklist << "xfdesktop"
|
||||||
<< "xfce4-panel"; // Xfce 4
|
<< "xfce4-panel"; // Xfce 4
|
||||||
|
|
||||||
m_currentGlobalKey = static_cast<Qt::Key>(0);
|
|
||||||
m_currentGlobalModifiers = nullptr;
|
|
||||||
|
|
||||||
m_keysymTable = nullptr;
|
m_keysymTable = nullptr;
|
||||||
m_xkb = nullptr;
|
m_xkb = nullptr;
|
||||||
m_remapKeycode = 0;
|
m_remapKeycode = 0;
|
||||||
m_currentRemapKeysym = NoSymbol;
|
m_currentRemapKeysym = NoSymbol;
|
||||||
m_modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
|
|
||||||
|
|
||||||
m_loaded = true;
|
m_loaded = true;
|
||||||
|
|
||||||
|
connect(nixUtils(), &NixUtils::keymapChanged, this, [this] { updateKeymap(); });
|
||||||
updateKeymap();
|
updateKeymap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,105 +130,6 @@ QString AutoTypePlatformX11::activeWindowTitle()
|
||||||
return windowTitle(activeWindow(), true);
|
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()
|
AutoTypeExecutor* AutoTypePlatformX11::createExecutor()
|
||||||
{
|
{
|
||||||
return new AutoTypeExecutorX11(this);
|
return new AutoTypeExecutorX11(this);
|
||||||
|
@ -395,89 +284,6 @@ bool AutoTypePlatformX11::isTopLevelWindow(Window window)
|
||||||
return result;
|
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.
|
* Update the keyboard and modifier mapping.
|
||||||
* We need the KeyboardMapping for AddKeysym.
|
* We need the KeyboardMapping for AddKeysym.
|
||||||
|
@ -491,8 +297,9 @@ void AutoTypePlatformX11::updateKeymap()
|
||||||
m_xkb = getKeyboard();
|
m_xkb = getKeyboard();
|
||||||
|
|
||||||
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
|
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
|
||||||
if (m_keysymTable != nullptr)
|
if (m_keysymTable != nullptr) {
|
||||||
XFree(m_keysymTable);
|
XFree(m_keysymTable);
|
||||||
|
}
|
||||||
m_keysymTable = XGetKeyboardMapping(m_dpy, m_minKeycode, m_maxKeycode - m_minKeycode + 1, &m_keysymPerKeycode);
|
m_keysymTable = XGetKeyboardMapping(m_dpy, m_minKeycode, m_maxKeycode - m_minKeycode + 1, &m_keysymPerKeycode);
|
||||||
|
|
||||||
/* determine the keycode to use for remapped keys */
|
/* 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
|
/* Xlib needs some time until the mapping is distributed to
|
||||||
all clients */
|
all clients */
|
||||||
// TODO: we should probably only sleep while in the middle of typing something
|
Tools::sleep(30);
|
||||||
timespec ts;
|
|
||||||
ts.tv_sec = 0;
|
|
||||||
ts.tv_nsec = 30 * 1000 * 1000;
|
|
||||||
nanosleep(&ts, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoTypePlatformX11::isRemapKeycodeValid()
|
bool AutoTypePlatformX11::isRemapKeycodeValid()
|
||||||
|
@ -542,36 +345,6 @@ bool AutoTypePlatformX11::isRemapKeycodeValid()
|
||||||
return false;
|
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()
|
XkbDescPtr AutoTypePlatformX11::getKeyboard()
|
||||||
{
|
{
|
||||||
int num_devices;
|
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)
|
* window to simulate keyboard. If modifiers (shift, control, etc)
|
||||||
* are set ON, many events will be sent.
|
* 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) {
|
if (keysym == NoSymbol) {
|
||||||
qWarning("No such key: keysym=0x%lX", keysym);
|
qWarning("No such key: keysym=0x%lX", keysym);
|
||||||
|
@ -798,12 +571,12 @@ AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform)
|
||||||
|
|
||||||
void AutoTypeExecutorX11::execChar(AutoTypeChar* action)
|
void AutoTypeExecutorX11::execChar(AutoTypeChar* action)
|
||||||
{
|
{
|
||||||
m_platform->SendKey(m_platform->charToKeySym(action->character));
|
m_platform->sendKey(qcharToNativeKeyCode(action->character));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoTypeExecutorX11::execKey(AutoTypeKey* action)
|
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)
|
void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||||
|
@ -814,13 +587,13 @@ void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||||
ts.tv_sec = 0;
|
ts.tv_sec = 0;
|
||||||
ts.tv_nsec = 25 * 1000 * 1000;
|
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);
|
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);
|
nanosleep(&ts, nullptr);
|
||||||
|
|
||||||
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_Backspace));
|
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_Backspace));
|
||||||
nanosleep(&ts, nullptr);
|
nanosleep(&ts, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,16 @@
|
||||||
#include <QX11Info>
|
#include <QX11Info>
|
||||||
#include <QtPlugin>
|
#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/XKBlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/extensions/XTest.h>
|
#include <X11/extensions/XTest.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
#include "autotype/AutoTypeAction.h"
|
|
||||||
#include "autotype/AutoTypePlatformPlugin.h"
|
|
||||||
|
|
||||||
#define N_MOD_INDICES (Mod5MapIndex + 1)
|
#define N_MOD_INDICES (Mod5MapIndex + 1)
|
||||||
|
|
||||||
|
@ -48,19 +52,10 @@ public:
|
||||||
QStringList windowTitles() override;
|
QStringList windowTitles() override;
|
||||||
WId activeWindow() override;
|
WId activeWindow() override;
|
||||||
QString activeWindowTitle() 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;
|
bool raiseWindow(WId window) override;
|
||||||
AutoTypeExecutor* createExecutor() override;
|
AutoTypeExecutor* createExecutor() override;
|
||||||
|
|
||||||
KeySym charToKeySym(const QChar& ch);
|
void sendKey(KeySym keysym, unsigned int modifiers = 0);
|
||||||
KeySym keyToKeySym(Qt::Key key);
|
|
||||||
|
|
||||||
void SendKey(KeySym keysym, unsigned int modifiers = 0);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void globalShortcutTriggered();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString windowTitle(Window window, bool useBlacklist);
|
QString windowTitle(Window window, bool useBlacklist);
|
||||||
|
@ -68,10 +63,6 @@ private:
|
||||||
QString windowClassName(Window window);
|
QString windowClassName(Window window);
|
||||||
QList<Window> widgetsToX11Windows(const QWidgetList& widgetList);
|
QList<Window> widgetsToX11Windows(const QWidgetList& widgetList);
|
||||||
bool isTopLevelWindow(Window window);
|
bool isTopLevelWindow(Window window);
|
||||||
uint qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
|
||||||
void startCatchXErrors();
|
|
||||||
void stopCatchXErrors();
|
|
||||||
static int x11ErrorHandler(Display* display, XErrorEvent* error);
|
|
||||||
|
|
||||||
XkbDescPtr getKeyboard();
|
XkbDescPtr getKeyboard();
|
||||||
void updateKeymap();
|
void updateKeymap();
|
||||||
|
@ -94,18 +85,6 @@ private:
|
||||||
Atom m_atomUtf8String;
|
Atom m_atomUtf8String;
|
||||||
Atom m_atomNetActiveWindow;
|
Atom m_atomNetActiveWindow;
|
||||||
QSet<QString> m_classBlacklist;
|
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;
|
XkbDescPtr m_xkb;
|
||||||
KeySym* m_keysymTable;
|
KeySym* m_keysymTable;
|
||||||
|
|
|
@ -36,10 +36,6 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#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)
|
#if defined(Q_OS_UNIX)
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
@ -60,9 +56,7 @@ Application::Application(int& argc, char** argv)
|
||||||
, m_alreadyRunning(false)
|
, m_alreadyRunning(false)
|
||||||
, m_lockFile(nullptr)
|
, m_lockFile(nullptr)
|
||||||
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||||
, m_osEventFilter(new OSEventFilter())
|
|
||||||
{
|
{
|
||||||
installNativeEventFilter(m_osEventFilter.data());
|
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
@ -160,6 +154,7 @@ void Application::bootstrap()
|
||||||
QApplication::setFont(QApplication::font("QMessageBox"));
|
QApplication::setFont(QApplication::font("QMessageBox"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
osUtils->registerNativeEventFilter();
|
||||||
MessageBox::initializeButtonDefs();
|
MessageBox::initializeButtonDefs();
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
|
|
|
@ -81,9 +81,6 @@ private:
|
||||||
QLockFile* m_lockFile;
|
QLockFile* m_lockFile;
|
||||||
QLocalServer m_lockServer;
|
QLocalServer m_lockServer;
|
||||||
QString m_socketName;
|
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())
|
#define kpxcApp qobject_cast<Application*>(Application::instance())
|
||||||
|
|
|
@ -60,7 +60,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||||
m_toggleVisibleAction = new QAction(
|
m_toggleVisibleAction = new QAction(
|
||||||
icons()->icon("password-show-off"),
|
icons()->icon("password-show-off"),
|
||||||
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
|
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
|
||||||
nullptr);
|
this);
|
||||||
m_toggleVisibleAction->setCheckable(true);
|
m_toggleVisibleAction->setCheckable(true);
|
||||||
m_toggleVisibleAction->setShortcut(modifier + Qt::Key_H);
|
m_toggleVisibleAction->setShortcut(modifier + Qt::Key_H);
|
||||||
m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut);
|
m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut);
|
||||||
|
@ -70,7 +70,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||||
m_passwordGeneratorAction = new QAction(
|
m_passwordGeneratorAction = new QAction(
|
||||||
icons()->icon("password-generator"),
|
icons()->icon("password-generator"),
|
||||||
tr("Generate Password (%1)").arg(QKeySequence(modifier + Qt::Key_G).toString(QKeySequence::NativeText)),
|
tr("Generate Password (%1)").arg(QKeySequence(modifier + Qt::Key_G).toString(QKeySequence::NativeText)),
|
||||||
nullptr);
|
this);
|
||||||
m_passwordGeneratorAction->setShortcut(modifier + Qt::Key_G);
|
m_passwordGeneratorAction->setShortcut(modifier + Qt::Key_G);
|
||||||
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
|
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
|
||||||
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
|
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
|
||||||
|
@ -79,7 +79,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||||
m_capslockAction =
|
m_capslockAction =
|
||||||
new QAction(icons()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
|
new QAction(icons()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
|
||||||
tr("Warning: Caps Lock enabled!"),
|
tr("Warning: Caps Lock enabled!"),
|
||||||
nullptr);
|
this);
|
||||||
addAction(m_capslockAction, QLineEdit::LeadingPosition);
|
addAction(m_capslockAction, QLineEdit::LeadingPosition);
|
||||||
m_capslockAction->setVisible(false);
|
m_capslockAction->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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
|
|
|
@ -50,6 +50,17 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual bool isCapslockEnabled() = 0;
|
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:
|
protected:
|
||||||
explicit OSUtilsBase(QObject* parent = nullptr);
|
explicit OSUtilsBase(QObject* parent = nullptr);
|
||||||
virtual ~OSUtilsBase();
|
virtual ~OSUtilsBase();
|
||||||
|
|
|
@ -23,8 +23,10 @@
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
#include <CoreGraphics/CGEventSource.h>
|
#include <CoreGraphics/CGEventSource.h>
|
||||||
|
|
||||||
|
#define INVALID_KEYCODE 0xFFFF
|
||||||
|
|
||||||
QPointer<MacUtils> MacUtils::m_instance = nullptr;
|
QPointer<MacUtils> MacUtils::m_instance = nullptr;
|
||||||
|
|
||||||
|
@ -95,9 +97,10 @@ bool MacUtils::isDarkMode() const
|
||||||
|
|
||||||
QString MacUtils::getLaunchAgentFilename() const
|
QString MacUtils::getLaunchAgentFilename() const
|
||||||
{
|
{
|
||||||
auto launchAgentDir = QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
|
auto launchAgentDir =
|
||||||
return QFile(launchAgentDir.absoluteFilePath(
|
QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
|
||||||
qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist"))).fileName();
|
return QFile(launchAgentDir.absoluteFilePath(qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist")))
|
||||||
|
.fileName();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MacUtils::isLaunchAtStartupEnabled() const
|
bool MacUtils::isLaunchAtStartupEnabled() const
|
||||||
|
@ -134,3 +137,306 @@ void MacUtils::toggleForegroundApp(bool foreground)
|
||||||
{
|
{
|
||||||
m_appkit->toggleForegroundApp(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;
|
||||||
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@
|
||||||
#ifndef KEEPASSXC_MACUTILS_H
|
#ifndef KEEPASSXC_MACUTILS_H
|
||||||
#define KEEPASSXC_MACUTILS_H
|
#define KEEPASSXC_MACUTILS_H
|
||||||
|
|
||||||
#include "gui/osutils/OSUtilsBase.h"
|
|
||||||
#include "AppKit.h"
|
#include "AppKit.h"
|
||||||
|
#include "gui/osutils/OSUtilsBase.h"
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
|
@ -48,6 +49,17 @@ public:
|
||||||
bool enableScreenRecording();
|
bool enableScreenRecording();
|
||||||
void toggleForegroundApp(bool foreground);
|
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:
|
signals:
|
||||||
void lockDatabases();
|
void lockDatabases();
|
||||||
|
|
||||||
|
@ -57,10 +69,22 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString getLaunchAgentFilename() const;
|
QString getLaunchAgentFilename() const;
|
||||||
|
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
|
||||||
|
|
||||||
QScopedPointer<AppKit> m_appkit;
|
QScopedPointer<AppKit> m_appkit;
|
||||||
static QPointer<MacUtils> m_instance;
|
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)
|
Q_DISABLE_COPY(MacUtils)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
* Automatically generated by keysymmap.py from parsing keysymdef.h.
|
* Automatically generated by keysymmap.py from parsing keysymdef.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const int AutoTypePlatformX11::m_unicodeToKeysymLen = 632;
|
const int unicodeToKeysymLen = 632;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {
|
const uint unicodeToKeysymKeys[] = {
|
||||||
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
|
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
|
||||||
0x0108, 0x0109, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, 0x010f,
|
0x0108, 0x0109, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, 0x010f,
|
||||||
0x0110, 0x0111, 0x0112, 0x0113, 0x0116, 0x0117, 0x0118, 0x0119,
|
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
|
0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f2, 0x30f3, 0x30fb, 0x30fc
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {
|
const uint unicodeToKeysymValues[] = {
|
||||||
0x03c0, 0x03e0, 0x01c3, 0x01e3, 0x01a1, 0x01b1, 0x01c6, 0x01e6,
|
0x03c0, 0x03e0, 0x01c3, 0x01e3, 0x01a1, 0x01b1, 0x01c6, 0x01e6,
|
||||||
0x02c6, 0x02e6, 0x02c5, 0x02e5, 0x01c8, 0x01e8, 0x01cf, 0x01ef,
|
0x02c6, 0x02e6, 0x02c5, 0x02e5, 0x01c8, 0x01e8, 0x01cf, 0x01ef,
|
||||||
0x01d0, 0x01f0, 0x03aa, 0x03ba, 0x03cc, 0x03ec, 0x01ca, 0x01ea,
|
0x01d0, 0x01f0, 0x03aa, 0x03ba, 0x03cc, 0x03ec, 0x01ca, 0x01ea,
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "NixUtils.h"
|
#include "NixUtils.h"
|
||||||
|
#include "KeySymMap.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
@ -26,13 +28,26 @@
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QStyle>
|
#include <QStyle>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
#include <QtX11Extras/QX11Info>
|
||||||
|
|
||||||
#include <qpa/qplatformnativeinterface.h>
|
#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 <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;
|
QPointer<NixUtils> NixUtils::m_instance = nullptr;
|
||||||
|
|
||||||
|
@ -48,6 +63,8 @@ NixUtils* NixUtils::instance()
|
||||||
NixUtils::NixUtils(QObject* parent)
|
NixUtils::NixUtils(QObject* parent)
|
||||||
: OSUtilsBase(parent)
|
: OSUtilsBase(parent)
|
||||||
{
|
{
|
||||||
|
dpy = QX11Info::display();
|
||||||
|
rootWindow = QX11Info::appRootWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
NixUtils::~NixUtils()
|
NixUtils::~NixUtils()
|
||||||
|
@ -125,7 +142,7 @@ bool NixUtils::isCapslockEnabled()
|
||||||
QString platform = QGuiApplication::platformName();
|
QString platform = QGuiApplication::platformName();
|
||||||
if (platform == "xcb") {
|
if (platform == "xcb") {
|
||||||
unsigned state = 0;
|
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);
|
return ((state & 1u) != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,3 +151,119 @@ bool NixUtils::isCapslockEnabled()
|
||||||
|
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -19,9 +19,10 @@
|
||||||
#define KEEPASSXC_NIXUTILS_H
|
#define KEEPASSXC_NIXUTILS_H
|
||||||
|
|
||||||
#include "gui/osutils/OSUtilsBase.h"
|
#include "gui/osutils/OSUtilsBase.h"
|
||||||
|
#include <QAbstractNativeEventFilter>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
|
||||||
class NixUtils : public OSUtilsBase
|
class NixUtils : public OSUtilsBase, QAbstractNativeEventFilter
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -33,15 +34,35 @@ public:
|
||||||
void setLaunchAtStartup(bool enable) override;
|
void setLaunchAtStartup(bool enable) override;
|
||||||
bool isCapslockEnabled() 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:
|
private:
|
||||||
explicit NixUtils(QObject* parent = nullptr);
|
explicit NixUtils(QObject* parent = nullptr);
|
||||||
~NixUtils() override;
|
~NixUtils() override;
|
||||||
|
|
||||||
private:
|
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
||||||
QString getAutostartDesktopFilename(bool createDirs = false) const;
|
QString getAutostartDesktopFilename(bool createDirs = false) const;
|
||||||
|
|
||||||
|
bool triggerGlobalShortcut(uint keycode, uint modifiers);
|
||||||
|
|
||||||
static QPointer<NixUtils> m_instance;
|
static QPointer<NixUtils> m_instance;
|
||||||
|
|
||||||
|
struct globalShortcut
|
||||||
|
{
|
||||||
|
uint nativeKeyCode;
|
||||||
|
uint nativeModifiers;
|
||||||
|
};
|
||||||
|
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
|
||||||
|
|
||||||
Q_DISABLE_COPY(NixUtils)
|
Q_DISABLE_COPY(NixUtils)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
126
src/gui/osutils/nixutils/X11Funcs.cpp
Normal file
126
src/gui/osutils/nixutils/X11Funcs.cpp
Normal 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;
|
||||||
|
}
|
30
src/gui/osutils/nixutils/X11Funcs.h
Normal file
30
src/gui/osutils/nixutils/X11Funcs.h
Normal 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
|
6
src/autotype/xcb/keysymmap.py → src/gui/osutils/nixutils/keysymmap.py
Executable file → Normal file
6
src/autotype/xcb/keysymmap.py → src/gui/osutils/nixutils/keysymmap.py
Executable file → Normal 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()
|
||||||
|
|
||||||
print("const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {")
|
print("const uint unicodeToKeysymKeys[] = {")
|
||||||
keys = keysymMap.keys()
|
keys = keysymMap.keys()
|
||||||
keyLen = len(keys)
|
keyLen = len(keys)
|
||||||
for idx, val in enumerate(keys, start=1):
|
for idx, val in enumerate(keys, start=1):
|
||||||
|
@ -84,7 +84,7 @@ print("};")
|
||||||
|
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print("const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {")
|
print("const uint unicodeToKeysymValues[] = {")
|
||||||
values = keysymMap.values()
|
values = keysymMap.values()
|
||||||
valuesLen = len(values)
|
valuesLen = len(values)
|
||||||
for idx, val in enumerate(values, start=1):
|
for idx, val in enumerate(values, start=1):
|
|
@ -24,7 +24,6 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
QPointer<WinUtils> WinUtils::m_instance = nullptr;
|
QPointer<WinUtils> WinUtils::m_instance = nullptr;
|
||||||
QScopedPointer<WinUtils::DWMEventFilter> WinUtils::m_eventFilter;
|
|
||||||
|
|
||||||
WinUtils* WinUtils::instance()
|
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) {
|
qApp->installNativeEventFilter(this);
|
||||||
m_eventFilter.reset(new DWMEventFilter);
|
|
||||||
qApp->installNativeEventFilter(m_eventFilter.data());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WinUtils::DWMEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long*)
|
bool WinUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
|
||||||
{
|
{
|
||||||
if (eventType != "windows_generic_MSG") {
|
if (eventType != "windows_generic_MSG") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* msg = static_cast<MSG*>(message);
|
auto* msg = static_cast<MSG*>(message);
|
||||||
if (!msg->hwnd) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (msg->message) {
|
switch (msg->message) {
|
||||||
|
/* TODO: indicate dark mode support for black title bar
|
||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
case WM_INITDIALOG: {
|
case WM_INITDIALOG: {
|
||||||
if (winUtils()->isDarkMode()) {
|
if (msg->hwnd && winUtils()->isDarkMode()) {
|
||||||
// TODO: indicate dark mode support for black title bar
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
case WM_HOTKEY:
|
||||||
|
triggerGlobalShortcut(msg->wParam);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -119,3 +113,319 @@ bool WinUtils::isHighContrastMode() const
|
||||||
QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat);
|
QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat);
|
||||||
return (settings.value("Flags").toInt() & 1u) != 0;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -23,14 +23,16 @@
|
||||||
#include <QAbstractNativeEventFilter>
|
#include <QAbstractNativeEventFilter>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
class WinUtils : public OSUtilsBase
|
#include <windef.h>
|
||||||
|
|
||||||
|
class WinUtils : public OSUtilsBase, QAbstractNativeEventFilter
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static WinUtils* instance();
|
static WinUtils* instance();
|
||||||
static void registerEventFilters();
|
|
||||||
|
|
||||||
bool isDarkMode() const override;
|
bool isDarkMode() const override;
|
||||||
bool isLaunchAtStartupEnabled() const override;
|
bool isLaunchAtStartupEnabled() const override;
|
||||||
|
@ -38,19 +40,36 @@ public:
|
||||||
bool isCapslockEnabled() override;
|
bool isCapslockEnabled() override;
|
||||||
bool isHighContrastMode() const;
|
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:
|
protected:
|
||||||
explicit WinUtils(QObject* parent = nullptr);
|
explicit WinUtils(QObject* parent = nullptr);
|
||||||
~WinUtils() override;
|
~WinUtils() override = default;
|
||||||
|
|
||||||
|
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
||||||
|
void triggerGlobalShortcut(int id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class DWMEventFilter : public QAbstractNativeEventFilter
|
static QPointer<WinUtils> m_instance;
|
||||||
|
|
||||||
|
struct globalShortcut
|
||||||
{
|
{
|
||||||
public:
|
int id;
|
||||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
DWORD nativeKeyCode;
|
||||||
|
DWORD nativeModifiers;
|
||||||
};
|
};
|
||||||
|
|
||||||
static QPointer<WinUtils> m_instance;
|
int m_nextShortcutId = 1;
|
||||||
static QScopedPointer<DWMEventFilter> m_eventFilter;
|
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
|
||||||
|
|
||||||
Q_DISABLE_COPY(WinUtils)
|
Q_DISABLE_COPY(WinUtils)
|
||||||
};
|
};
|
||||||
|
|
|
@ -119,8 +119,6 @@ void DarkStyle::polish(QWidget* widget)
|
||||||
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x252525));
|
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x252525));
|
||||||
}
|
}
|
||||||
#elif defined(Q_OS_WIN)
|
#elif defined(Q_OS_WIN)
|
||||||
// Register event filter for better dark mode support
|
|
||||||
WinUtils::registerEventFilters();
|
|
||||||
palette.setColor(QPalette::All, QPalette::Window, QRgb(0x2F2F30));
|
palette.setColor(QPalette::All, QPalette::Window, QRgb(0x2F2F30));
|
||||||
#else
|
#else
|
||||||
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x2F2F30));
|
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x2F2F30));
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "core/Resources.h"
|
#include "core/Resources.h"
|
||||||
#include "crypto/Crypto.h"
|
#include "crypto/Crypto.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
|
#include "gui/osutils/OSUtils.h"
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestAutoType)
|
QTEST_GUILESS_MAIN(TestAutoType)
|
||||||
|
|
||||||
|
@ -157,7 +158,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch()
|
||||||
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
||||||
{
|
{
|
||||||
m_test->setActiveWindowTitle("custom window");
|
m_test->setActiveWindowTitle("custom window");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
|
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);
|
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("An Entry Title!");
|
m_test->setActiveWindowTitle("An Entry Title!");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
|
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);
|
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
|
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
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);
|
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
|
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
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()
|
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
|
||||||
{
|
{
|
||||||
m_test->setActiveWindowTitle("An Entry Title!");
|
m_test->setActiveWindowTitle("An Entry Title!");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
MessageBox::setNextAnswer(MessageBox::Ok);
|
MessageBox::setNextAnswer(MessageBox::Ok);
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
|
@ -210,68 +211,68 @@ void TestAutoType::testGlobalAutoTypeRegExp()
|
||||||
{
|
{
|
||||||
// substring matches are ok
|
// substring matches are ok
|
||||||
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// should be case-insensitive
|
// should be case-insensitive
|
||||||
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// exact match
|
// exact match
|
||||||
m_test->setActiveWindowTitle("REGEX2");
|
m_test->setActiveWindowTitle("REGEX2");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// a bit more complicated regex
|
// a bit more complicated regex
|
||||||
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with custom attributes
|
// with custom attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr1");
|
m_test->setActiveWindowTitle("CustomAttr1");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with (non uppercase) undefined custom attributes
|
// with (non uppercase) undefined custom attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr2");
|
m_test->setActiveWindowTitle("CustomAttr2");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString(""));
|
QCOMPARE(m_test->actionChars(), QString(""));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with mixedcase default attributes
|
// with mixedcase default attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr3");
|
m_test->setActiveWindowTitle("CustomAttr3");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with resolve placeholders in window association title
|
// with resolve placeholders in window association title
|
||||||
m_test->setActiveWindowTitle("AttrValueFirst");
|
m_test->setActiveWindowTitle("AttrValueFirst");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
|
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
|
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
|
||||||
m_test->triggerGlobalAutoType();
|
emit osUtils->globalShortcutTriggered("autotype");
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
|
@ -70,13 +70,13 @@ void TestCli::initTestCase()
|
||||||
Config::createTempFileInstance();
|
Config::createTempFileInstance();
|
||||||
Bootstrap::bootstrap();
|
Bootstrap::bootstrap();
|
||||||
|
|
||||||
auto fd = new QFile();
|
m_devNull.reset(new QFile());
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
fd->open(fopen("nul", "w"), QIODevice::WriteOnly);
|
m_devNull->open(fopen("nul", "w"), QIODevice::WriteOnly);
|
||||||
#else
|
#else
|
||||||
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
m_devNull->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
||||||
#endif
|
#endif
|
||||||
Utils::DEVNULL.setDevice(fd);
|
Utils::DEVNULL.setDevice(m_devNull.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCli::init()
|
void TestCli::init()
|
||||||
|
@ -131,6 +131,11 @@ void TestCli::cleanup()
|
||||||
Utils::STDIN.setDevice(nullptr);
|
Utils::STDIN.setDevice(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestCli::cleanupTestCase()
|
||||||
|
{
|
||||||
|
m_devNull.reset();
|
||||||
|
}
|
||||||
|
|
||||||
QSharedPointer<Database> TestCli::readDatabase(const QString& filename, const QString& pw, const QString& keyfile)
|
QSharedPointer<Database> TestCli::readDatabase(const QString& filename, const QString& pw, const QString& keyfile)
|
||||||
{
|
{
|
||||||
auto db = QSharedPointer<Database>::create();
|
auto db = QSharedPointer<Database>::create();
|
||||||
|
@ -520,8 +525,7 @@ void TestCli::testClip()
|
||||||
|
|
||||||
setInput("a");
|
setInput("a");
|
||||||
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "-a", "TESTAttribute1", "/Sample Entry"});
|
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "-a", "TESTAttribute1", "/Sample Entry"});
|
||||||
QVERIFY(m_stderr->readAll().contains(
|
QVERIFY(m_stderr->readAll().contains("ERROR: attribute TESTAttribute1 is ambiguous"));
|
||||||
"ERROR: attribute TESTAttribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
|
|
||||||
|
|
||||||
setInput("a");
|
setInput("a");
|
||||||
execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry"});
|
execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry"});
|
||||||
|
@ -1776,8 +1780,7 @@ void TestCli::testShow()
|
||||||
setInput("a");
|
setInput("a");
|
||||||
execCmd(showCmd, {"show", m_dbFile->fileName(), "-a", "Testattribute1", "/Sample Entry"});
|
execCmd(showCmd, {"show", m_dbFile->fileName(), "-a", "Testattribute1", "/Sample Entry"});
|
||||||
QCOMPARE(m_stdout->readAll(), QByteArray());
|
QCOMPARE(m_stdout->readAll(), QByteArray());
|
||||||
QVERIFY(m_stderr->readAll().contains(
|
QVERIFY(m_stderr->readAll().contains("ERROR: attribute Testattribute1 is ambiguous"));
|
||||||
"ERROR: attribute Testattribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestCli::testInvalidDbFiles()
|
void TestCli::testInvalidDbFiles()
|
||||||
|
|
|
@ -45,6 +45,7 @@ private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void init();
|
void init();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
void testBatchCommands();
|
void testBatchCommands();
|
||||||
void testAdd();
|
void testAdd();
|
||||||
|
@ -81,6 +82,7 @@ private slots:
|
||||||
void testYubiKeyOption();
|
void testYubiKeyOption();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QScopedPointer<QFile> m_devNull;
|
||||||
QScopedPointer<TemporaryFile> m_dbFile;
|
QScopedPointer<TemporaryFile> m_dbFile;
|
||||||
QScopedPointer<TemporaryFile> m_dbFile2;
|
QScopedPointer<TemporaryFile> m_dbFile2;
|
||||||
QScopedPointer<TemporaryFile> m_dbFileMulti;
|
QScopedPointer<TemporaryFile> m_dbFileMulti;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue