mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-03-25 23:48:18 -04:00
Move global shortcut handling into OSUtils (#5566)
Move global shortcut handling into OSUtils
This commit is contained in:
parent
a6f01349e8
commit
404fd941e8
@ -305,11 +305,11 @@ if(WITH_COVERAGE AND CMAKE_COMPILER_IS_CLANGXX)
|
||||
# `find src -iname '*.h' -or -iname '*.cpp'`
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(UNIX AND NOT APPLE)
|
||||
check_add_gcc_compiler_flag("-Qunused-arguments")
|
||||
add_gcc_compiler_flags("-pie -fPIE")
|
||||
check_add_gcc_compiler_flag("-fPIC")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro,-z,now -pie")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-z,relro,-z,now")
|
||||
endif()
|
||||
@ -399,7 +399,7 @@ include(CLangFormat)
|
||||
|
||||
set(QT_COMPONENTS Core Network Concurrent Gui Svg Widgets Test LinguistTools)
|
||||
if(UNIX AND NOT APPLE)
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus REQUIRED)
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} DBus X11Extras REQUIRED)
|
||||
elseif(APPLE)
|
||||
find_package(Qt5 COMPONENTS ${QT_COMPONENTS} REQUIRED HINTS /usr/local/opt/qt/lib/cmake /usr/local/Cellar/qt/*/lib/cmake ENV PATH)
|
||||
find_package(Qt5 COMPONENTS MacExtras HINTS /usr/local/opt/qt/lib/cmake /usr/local/Cellar/qt/*/lib/cmake ENV PATH)
|
||||
|
@ -206,7 +206,8 @@ if(UNIX AND NOT APPLE)
|
||||
${keepassx_SOURCES}
|
||||
gui/MainWindowAdaptor.cpp
|
||||
gui/osutils/nixutils/ScreenLockListenerDBus.cpp
|
||||
gui/osutils/nixutils/NixUtils.cpp)
|
||||
gui/osutils/nixutils/NixUtils.cpp
|
||||
gui/osutils/nixutils/X11Funcs.cpp)
|
||||
endif()
|
||||
if(MINGW)
|
||||
set(keepassx_SOURCES
|
||||
@ -214,11 +215,6 @@ if(MINGW)
|
||||
gui/osutils/winutils/ScreenLockListenerWin.cpp
|
||||
gui/osutils/winutils/WinUtils.cpp)
|
||||
endif()
|
||||
if(MINGW OR (UNIX AND NOT APPLE))
|
||||
set(keepassx_SOURCES
|
||||
${keepassx_SOURCES}
|
||||
gui/osutils/OSEventFilter.cpp)
|
||||
endif()
|
||||
|
||||
set(keepassx_SOURCES ${keepassx_SOURCES}
|
||||
../share/icons/icons.qrc
|
||||
@ -343,7 +339,7 @@ if(WITH_XC_KEESHARE)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_link_libraries(keepassx_core "-framework Foundation -framework AppKit")
|
||||
target_link_libraries(keepassx_core "-framework Foundation -framework AppKit -framework Carbon")
|
||||
if(Qt5MacExtras_FOUND)
|
||||
target_link_libraries(keepassx_core Qt5::MacExtras)
|
||||
endif()
|
||||
@ -356,7 +352,7 @@ if(HAIKU)
|
||||
target_link_libraries(keepassx_core network)
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(keepassx_core Qt5::DBus X11)
|
||||
target_link_libraries(keepassx_core Qt5::DBus Qt5::X11Extras X11)
|
||||
include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
if(MINGW)
|
||||
|
@ -37,18 +37,13 @@
|
||||
#include "core/Tools.h"
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/MessageBox.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "gui/osutils/macutils/MacUtils.h"
|
||||
#endif
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
AutoType* AutoType::m_instance = nullptr;
|
||||
|
||||
AutoType::AutoType(QObject* parent, bool test)
|
||||
: QObject(parent)
|
||||
, m_autoTypeDelay(0)
|
||||
, m_currentGlobalKey(static_cast<Qt::Key>(0))
|
||||
, m_currentGlobalModifiers(nullptr)
|
||||
, m_pluginLoader(new QPluginLoader(this))
|
||||
, m_plugin(nullptr)
|
||||
, m_executor(nullptr)
|
||||
@ -96,7 +91,11 @@ void AutoType::loadPlugin(const QString& pluginPath)
|
||||
if (m_plugin) {
|
||||
if (m_plugin->isAvailable()) {
|
||||
m_executor = m_plugin->createExecutor();
|
||||
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType()));
|
||||
connect(osUtils, &OSUtilsBase::globalShortcutTriggered, this, [this](QString name) {
|
||||
if (name == "autotype") {
|
||||
startGlobalAutoType();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
unloadPlugin();
|
||||
}
|
||||
@ -153,44 +152,18 @@ void AutoType::raiseWindow()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
||||
{
|
||||
Q_ASSERT(key);
|
||||
Q_ASSERT(modifiers);
|
||||
|
||||
if (!m_plugin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (key != m_currentGlobalKey || modifiers != m_currentGlobalModifiers) {
|
||||
if (m_currentGlobalKey && m_currentGlobalModifiers) {
|
||||
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||
}
|
||||
|
||||
if (m_plugin->registerGlobalShortcut(key, modifiers)) {
|
||||
m_currentGlobalKey = key;
|
||||
m_currentGlobalModifiers = modifiers;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return osUtils->registerGlobalShortcut("autotype", key, modifiers, error);
|
||||
}
|
||||
|
||||
void AutoType::unregisterGlobalShortcut()
|
||||
{
|
||||
if (m_plugin && m_currentGlobalKey && m_currentGlobalModifiers) {
|
||||
m_plugin->unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||
}
|
||||
}
|
||||
|
||||
int AutoType::callEventFilter(void* event)
|
||||
{
|
||||
if (!m_plugin) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return m_plugin->platformEventFilter(event);
|
||||
osUtils->unregisterGlobalShortcut("autotype");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,6 +276,23 @@ void AutoType::startGlobalAutoType()
|
||||
m_windowForGlobal = m_plugin->activeWindow();
|
||||
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
|
||||
#ifdef Q_OS_MACOS
|
||||
// Determine if the user has given proper permissions to KeePassXC to perform Auto-Type
|
||||
static bool accessibilityChecked = false;
|
||||
if (!accessibilityChecked) {
|
||||
if (macUtils()->enableAccessibility() && macUtils()->enableScreenRecording()) {
|
||||
accessibilityChecked = true;
|
||||
} else if (getMainWindow()) {
|
||||
// Does not have required permissions to Auto-Type, ignore the event
|
||||
MessageBox::information(
|
||||
nullptr,
|
||||
tr("Permission Required"),
|
||||
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
|
||||
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
|
||||
"already granted permission, you may have to restart KeePassXC."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_windowState = WindowState::Normal;
|
||||
if (getMainWindow()) {
|
||||
if (getMainWindow()->isMinimized()) {
|
||||
|
@ -39,9 +39,8 @@ class AutoType : public QObject
|
||||
|
||||
public:
|
||||
QStringList windowTitles();
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
|
||||
void unregisterGlobalShortcut();
|
||||
int callEventFilter(void* event);
|
||||
static bool checkSyntax(const QString& string);
|
||||
static bool checkHighRepetition(const QString& string);
|
||||
static bool checkSlowKeypress(const QString& string);
|
||||
@ -99,8 +98,6 @@ private:
|
||||
QMutex m_inAutoType;
|
||||
QMutex m_inGlobalAutoTypeDialog;
|
||||
int m_autoTypeDelay;
|
||||
Qt::Key m_currentGlobalKey;
|
||||
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
||||
QPluginLoader* m_pluginLoader;
|
||||
AutoTypePlatformInterface* m_plugin;
|
||||
AutoTypeExecutor* m_executor;
|
||||
|
@ -32,9 +32,6 @@ public:
|
||||
virtual QStringList windowTitles() = 0;
|
||||
virtual WId activeWindow() = 0;
|
||||
virtual QString activeWindowTitle() = 0;
|
||||
virtual bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) = 0;
|
||||
virtual int platformEventFilter(void* event) = 0;
|
||||
virtual bool raiseWindow(WId window) = 0;
|
||||
virtual void unload()
|
||||
{
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "ShortcutWidget.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QToolTip>
|
||||
|
||||
#include "autotype/AutoType.h"
|
||||
|
||||
@ -48,9 +49,11 @@ void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
|
||||
displayShortcut(m_key, m_modifiers);
|
||||
|
||||
if (autoType()->registerGlobalShortcut(m_key, m_modifiers)) {
|
||||
QString error;
|
||||
if (autoType()->registerGlobalShortcut(m_key, m_modifiers, &error)) {
|
||||
setStyleSheet("");
|
||||
} else {
|
||||
QToolTip::showText(mapToGlobal(rect().bottomLeft()), error);
|
||||
setStyleSheet("background-color: #FF9696;");
|
||||
}
|
||||
}
|
||||
|
@ -22,24 +22,12 @@
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#define HOTKEY_ID 1
|
||||
#define MAX_WINDOW_TITLE_LENGTH 1024
|
||||
#define INVALID_KEYCODE 0xFFFF
|
||||
|
||||
namespace {
|
||||
bool accessibilityChecked = false;
|
||||
}
|
||||
|
||||
AutoTypePlatformMac::AutoTypePlatformMac()
|
||||
: m_hotkeyRef(nullptr)
|
||||
, m_hotkeyId({ 'kpx2', HOTKEY_ID })
|
||||
{
|
||||
EventTypeSpec eventSpec;
|
||||
eventSpec.eventClass = kEventClassKeyboard;
|
||||
eventSpec.eventKind = kEventHotKeyPressed;
|
||||
|
||||
MessageBox::initializeButtonDefs();
|
||||
::InstallApplicationEventHandler(AutoTypePlatformMac::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,44 +108,6 @@ QString AutoTypePlatformMac::activeWindowTitle()
|
||||
return title;
|
||||
}
|
||||
|
||||
//
|
||||
// Register global hotkey
|
||||
//
|
||||
bool AutoTypePlatformMac::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
uint16 nativeKeyCode = qtToNativeKeyCode(key);
|
||||
if (nativeKeyCode == INVALID_KEYCODE) {
|
||||
qWarning("Invalid key code");
|
||||
return false;
|
||||
}
|
||||
CGEventFlags nativeModifiers = qtToNativeModifiers(modifiers, false);
|
||||
if (::RegisterEventHotKey(nativeKeyCode, nativeModifiers, m_hotkeyId, GetApplicationEventTarget(), 0, &m_hotkeyRef) != noErr) {
|
||||
qWarning("Register hotkey failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Unregister global hotkey
|
||||
//
|
||||
void AutoTypePlatformMac::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
Q_UNUSED(modifiers);
|
||||
|
||||
::UnregisterEventHotKey(m_hotkeyRef);
|
||||
}
|
||||
|
||||
int AutoTypePlatformMac::platformEventFilter(void* event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
Q_ASSERT(false);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
AutoTypeExecutor* AutoTypePlatformMac::createExecutor()
|
||||
{
|
||||
return new AutoTypeExecutorMac(this);
|
||||
@ -208,13 +158,13 @@ void AutoTypePlatformMac::sendChar(const QChar& ch, bool isKeyDown)
|
||||
//
|
||||
void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers = 0)
|
||||
{
|
||||
uint16 keyCode = qtToNativeKeyCode(key);
|
||||
uint16 keyCode = macUtils()->qtToNativeKeyCode(key);
|
||||
if (keyCode == INVALID_KEYCODE) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventRef keyEvent = ::CGEventCreateKeyboardEvent(nullptr, keyCode, isKeyDown);
|
||||
CGEventFlags nativeModifiers = qtToNativeModifiers(modifiers, true);
|
||||
CGEventFlags nativeModifiers = macUtils()->qtToNativeModifiers(modifiers, true);
|
||||
if (keyEvent != nullptr) {
|
||||
::CGEventSetFlags(keyEvent, nativeModifiers);
|
||||
::CGEventPost(kCGSessionEventTap, keyEvent);
|
||||
@ -222,223 +172,6 @@ void AutoTypePlatformMac::sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModif
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Translate qt key code to mac os key code
|
||||
// see: HIToolbox/Events.h
|
||||
//
|
||||
uint16 AutoTypePlatformMac::qtToNativeKeyCode(Qt::Key key)
|
||||
{
|
||||
switch (key) {
|
||||
case Qt::Key_A:
|
||||
return kVK_ANSI_A;
|
||||
case Qt::Key_B:
|
||||
return kVK_ANSI_B;
|
||||
case Qt::Key_C:
|
||||
return kVK_ANSI_C;
|
||||
case Qt::Key_D:
|
||||
return kVK_ANSI_D;
|
||||
case Qt::Key_E:
|
||||
return kVK_ANSI_E;
|
||||
case Qt::Key_F:
|
||||
return kVK_ANSI_F;
|
||||
case Qt::Key_G:
|
||||
return kVK_ANSI_G;
|
||||
case Qt::Key_H:
|
||||
return kVK_ANSI_H;
|
||||
case Qt::Key_I:
|
||||
return kVK_ANSI_I;
|
||||
case Qt::Key_J:
|
||||
return kVK_ANSI_J;
|
||||
case Qt::Key_K:
|
||||
return kVK_ANSI_K;
|
||||
case Qt::Key_L:
|
||||
return kVK_ANSI_L;
|
||||
case Qt::Key_M:
|
||||
return kVK_ANSI_M;
|
||||
case Qt::Key_N:
|
||||
return kVK_ANSI_N;
|
||||
case Qt::Key_O:
|
||||
return kVK_ANSI_O;
|
||||
case Qt::Key_P:
|
||||
return kVK_ANSI_P;
|
||||
case Qt::Key_Q:
|
||||
return kVK_ANSI_Q;
|
||||
case Qt::Key_R:
|
||||
return kVK_ANSI_R;
|
||||
case Qt::Key_S:
|
||||
return kVK_ANSI_S;
|
||||
case Qt::Key_T:
|
||||
return kVK_ANSI_T;
|
||||
case Qt::Key_U:
|
||||
return kVK_ANSI_U;
|
||||
case Qt::Key_V:
|
||||
return kVK_ANSI_V;
|
||||
case Qt::Key_W:
|
||||
return kVK_ANSI_W;
|
||||
case Qt::Key_X:
|
||||
return kVK_ANSI_X;
|
||||
case Qt::Key_Y:
|
||||
return kVK_ANSI_Y;
|
||||
case Qt::Key_Z:
|
||||
return kVK_ANSI_Z;
|
||||
|
||||
case Qt::Key_0:
|
||||
return kVK_ANSI_0;
|
||||
case Qt::Key_1:
|
||||
return kVK_ANSI_1;
|
||||
case Qt::Key_2:
|
||||
return kVK_ANSI_2;
|
||||
case Qt::Key_3:
|
||||
return kVK_ANSI_3;
|
||||
case Qt::Key_4:
|
||||
return kVK_ANSI_4;
|
||||
case Qt::Key_5:
|
||||
return kVK_ANSI_5;
|
||||
case Qt::Key_6:
|
||||
return kVK_ANSI_6;
|
||||
case Qt::Key_7:
|
||||
return kVK_ANSI_7;
|
||||
case Qt::Key_8:
|
||||
return kVK_ANSI_8;
|
||||
case Qt::Key_9:
|
||||
return kVK_ANSI_9;
|
||||
|
||||
case Qt::Key_Equal:
|
||||
return kVK_ANSI_Equal;
|
||||
case Qt::Key_Minus:
|
||||
return kVK_ANSI_Minus;
|
||||
case Qt::Key_BracketRight:
|
||||
return kVK_ANSI_RightBracket;
|
||||
case Qt::Key_BracketLeft:
|
||||
return kVK_ANSI_LeftBracket;
|
||||
case Qt::Key_QuoteDbl:
|
||||
return kVK_ANSI_Quote;
|
||||
case Qt::Key_Semicolon:
|
||||
return kVK_ANSI_Semicolon;
|
||||
case Qt::Key_Backslash:
|
||||
return kVK_ANSI_Backslash;
|
||||
case Qt::Key_Comma:
|
||||
return kVK_ANSI_Comma;
|
||||
case Qt::Key_Slash:
|
||||
return kVK_ANSI_Slash;
|
||||
case Qt::Key_Period:
|
||||
return kVK_ANSI_Period;
|
||||
|
||||
case Qt::Key_Shift:
|
||||
return kVK_Shift;
|
||||
case Qt::Key_Control:
|
||||
return kVK_Command;
|
||||
case Qt::Key_Backspace:
|
||||
return kVK_Delete;
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
return kVK_Tab;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
return kVK_Return;
|
||||
case Qt::Key_CapsLock:
|
||||
return kVK_CapsLock;
|
||||
case Qt::Key_Escape:
|
||||
return kVK_Escape;
|
||||
case Qt::Key_Space:
|
||||
return kVK_Space;
|
||||
case Qt::Key_PageUp:
|
||||
return kVK_PageUp;
|
||||
case Qt::Key_PageDown:
|
||||
return kVK_PageDown;
|
||||
case Qt::Key_End:
|
||||
return kVK_End;
|
||||
case Qt::Key_Home:
|
||||
return kVK_Home;
|
||||
case Qt::Key_Left:
|
||||
return kVK_LeftArrow;
|
||||
case Qt::Key_Up:
|
||||
return kVK_UpArrow;
|
||||
case Qt::Key_Right:
|
||||
return kVK_RightArrow;
|
||||
case Qt::Key_Down:
|
||||
return kVK_DownArrow;
|
||||
case Qt::Key_Delete:
|
||||
return kVK_ForwardDelete;
|
||||
case Qt::Key_Help:
|
||||
return kVK_Help;
|
||||
|
||||
case Qt::Key_F1:
|
||||
return kVK_F1;
|
||||
case Qt::Key_F2:
|
||||
return kVK_F2;
|
||||
case Qt::Key_F3:
|
||||
return kVK_F3;
|
||||
case Qt::Key_F4:
|
||||
return kVK_F4;
|
||||
case Qt::Key_F5:
|
||||
return kVK_F5;
|
||||
case Qt::Key_F6:
|
||||
return kVK_F6;
|
||||
case Qt::Key_F7:
|
||||
return kVK_F7;
|
||||
case Qt::Key_F8:
|
||||
return kVK_F8;
|
||||
case Qt::Key_F9:
|
||||
return kVK_F9;
|
||||
case Qt::Key_F10:
|
||||
return kVK_F10;
|
||||
case Qt::Key_F11:
|
||||
return kVK_F11;
|
||||
case Qt::Key_F12:
|
||||
return kVK_F12;
|
||||
case Qt::Key_F13:
|
||||
return kVK_F13;
|
||||
case Qt::Key_F14:
|
||||
return kVK_F14;
|
||||
case Qt::Key_F15:
|
||||
return kVK_F15;
|
||||
case Qt::Key_F16:
|
||||
return kVK_F16;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return INVALID_KEYCODE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Translate qt key modifiers to mac os modifiers
|
||||
// see: https://doc.qt.io/qt-5/osx-issues.html#special-keys
|
||||
//
|
||||
CGEventFlags AutoTypePlatformMac::qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native)
|
||||
{
|
||||
CGEventFlags nativeModifiers = CGEventFlags(0);
|
||||
|
||||
CGEventFlags shiftMod = CGEventFlags(shiftKey);
|
||||
CGEventFlags cmdMod = CGEventFlags(cmdKey);
|
||||
CGEventFlags optionMod = CGEventFlags(optionKey);
|
||||
CGEventFlags controlMod = CGEventFlags(controlKey);
|
||||
|
||||
if (native) {
|
||||
shiftMod = kCGEventFlagMaskShift;
|
||||
cmdMod = kCGEventFlagMaskCommand;
|
||||
optionMod = kCGEventFlagMaskAlternate;
|
||||
controlMod = kCGEventFlagMaskControl;
|
||||
}
|
||||
|
||||
|
||||
if (modifiers & Qt::ShiftModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | shiftMod);
|
||||
}
|
||||
if (modifiers & Qt::ControlModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | cmdMod);
|
||||
}
|
||||
if (modifiers & Qt::AltModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | optionMod);
|
||||
}
|
||||
if (modifiers & Qt::MetaModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | controlMod);
|
||||
}
|
||||
|
||||
return nativeModifiers;
|
||||
}
|
||||
|
||||
//
|
||||
// Get window layer/level
|
||||
//
|
||||
@ -472,39 +205,6 @@ QString AutoTypePlatformMac::windowTitle(CFDictionaryRef window)
|
||||
return title;
|
||||
}
|
||||
|
||||
//
|
||||
// Carbon hotkey handler
|
||||
//
|
||||
OSStatus AutoTypePlatformMac::hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
|
||||
{
|
||||
Q_UNUSED(nextHandler);
|
||||
|
||||
// Determine if the user has given proper permissions to KeePassXC to perform Auto-Type
|
||||
if (!accessibilityChecked) {
|
||||
if (macUtils()->enableAccessibility() && macUtils()->enableScreenRecording()) {
|
||||
accessibilityChecked = true;
|
||||
} else {
|
||||
// Does not have required permissions to Auto-Type, ignore the keypress
|
||||
MessageBox::information(nullptr,
|
||||
tr("Permission Required"),
|
||||
tr("KeePassXC requires the Accessibility and Screen Recorder permission in order to perform global "
|
||||
"Auto-Type. Screen Recording is necessary to use the window title to find entries. If you "
|
||||
"already granted permission, you may have to restart KeePassXC."));
|
||||
return noErr;
|
||||
}
|
||||
}
|
||||
|
||||
AutoTypePlatformMac* self = static_cast<AutoTypePlatformMac*>(userData);
|
||||
EventHotKeyID hotkeyId;
|
||||
|
||||
if (::GetEventParameter(theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId) == noErr
|
||||
&& hotkeyId.id == HOTKEY_ID) {
|
||||
emit self->globalShortcutTriggered();
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
//
|
||||
// ------------------------------ AutoTypeExecutorMac ------------------------------
|
||||
//
|
||||
|
@ -38,9 +38,6 @@ public:
|
||||
QStringList windowTitles() override;
|
||||
WId activeWindow() override;
|
||||
QString activeWindowTitle() override;
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
bool raiseWindow(WId pid) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
@ -50,18 +47,9 @@ public:
|
||||
void sendChar(const QChar& ch, bool isKeyDown);
|
||||
void sendKey(Qt::Key key, bool isKeyDown, Qt::KeyboardModifiers modifiers);
|
||||
|
||||
signals:
|
||||
void globalShortcutTriggered();
|
||||
|
||||
private:
|
||||
EventHotKeyRef m_hotkeyRef;
|
||||
EventHotKeyID m_hotkeyId;
|
||||
|
||||
static uint16 qtToNativeKeyCode(Qt::Key key);
|
||||
static CGEventFlags qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native);
|
||||
static int windowLayer(CFDictionaryRef window);
|
||||
static QString windowTitle(CFDictionaryRef window);
|
||||
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
|
||||
};
|
||||
|
||||
class AutoTypeExecutorMac : public AutoTypeExecutor
|
||||
|
@ -42,37 +42,11 @@ QString AutoTypePlatformTest::activeWindowTitle()
|
||||
return m_activeWindowTitle;
|
||||
}
|
||||
|
||||
bool AutoTypePlatformTest::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
Q_UNUSED(modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoTypePlatformTest::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
Q_UNUSED(modifiers);
|
||||
}
|
||||
|
||||
int AutoTypePlatformTest::platformEventFilter(void* event)
|
||||
{
|
||||
Q_UNUSED(event);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
AutoTypeExecutor* AutoTypePlatformTest::createExecutor()
|
||||
{
|
||||
return new AutoTypeExecutorTest(this);
|
||||
}
|
||||
|
||||
void AutoTypePlatformTest::triggerGlobalAutoType()
|
||||
{
|
||||
emit globalShortcutTriggered();
|
||||
}
|
||||
|
||||
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
|
||||
{
|
||||
m_activeWindowTitle = title;
|
||||
|
@ -37,9 +37,6 @@ public:
|
||||
QStringList windowTitles() override;
|
||||
WId activeWindow() override;
|
||||
QString activeWindowTitle() override;
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
@ -48,7 +45,6 @@ public:
|
||||
bool raiseOwnWindow() override;
|
||||
#endif
|
||||
|
||||
void triggerGlobalAutoType() override;
|
||||
void setActiveWindowTitle(const QString& title) override;
|
||||
|
||||
QString actionChars() override;
|
||||
@ -58,9 +54,6 @@ public:
|
||||
void addActionChar(AutoTypeChar* action);
|
||||
void addActionKey(AutoTypeKey* action);
|
||||
|
||||
signals:
|
||||
void globalShortcutTriggered();
|
||||
|
||||
private:
|
||||
QString m_activeWindowTitle;
|
||||
QList<AutoTypeAction*> m_actionList;
|
||||
|
@ -26,7 +26,6 @@ public:
|
||||
virtual ~AutoTypeTestInterface()
|
||||
{
|
||||
}
|
||||
virtual void triggerGlobalAutoType() = 0;
|
||||
virtual void setActiveWindowTitle(const QString& title) = 0;
|
||||
|
||||
virtual QString actionChars() = 0;
|
||||
|
@ -17,6 +17,7 @@
|
||||
*/
|
||||
|
||||
#include "AutoTypeWindows.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
@ -61,49 +62,6 @@ QString AutoTypePlatformWin::activeWindowTitle()
|
||||
return windowTitle(::GetForegroundWindow());
|
||||
}
|
||||
|
||||
//
|
||||
// Register global hotkey
|
||||
//
|
||||
bool AutoTypePlatformWin::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
DWORD nativeKeyCode = qtToNativeKeyCode(key);
|
||||
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
||||
return false;
|
||||
}
|
||||
DWORD nativeModifiers = qtToNativeModifiers(modifiers);
|
||||
if (!::RegisterHotKey(nullptr, HOTKEY_ID, nativeModifiers | MOD_NOREPEAT, nativeKeyCode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Unregister global hotkey
|
||||
//
|
||||
void AutoTypePlatformWin::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
Q_UNUSED(key);
|
||||
Q_UNUSED(modifiers);
|
||||
|
||||
::UnregisterHotKey(nullptr, HOTKEY_ID);
|
||||
}
|
||||
|
||||
//
|
||||
// Native event filter
|
||||
//
|
||||
int AutoTypePlatformWin::platformEventFilter(void* event)
|
||||
{
|
||||
MSG* msg = static_cast<MSG*>(event);
|
||||
|
||||
if (msg->message == WM_HOTKEY && msg->wParam == HOTKEY_ID) {
|
||||
emit globalShortcutTriggered();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
AutoTypeExecutor* AutoTypePlatformWin::createExecutor()
|
||||
{
|
||||
return new AutoTypeExecutorWin(this);
|
||||
@ -145,7 +103,7 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
||||
//
|
||||
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||
{
|
||||
DWORD nativeKeyCode = qtToNativeKeyCode(key);
|
||||
DWORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
|
||||
if (nativeKeyCode < 1 || nativeKeyCode > 254) {
|
||||
return;
|
||||
}
|
||||
@ -168,234 +126,12 @@ void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||
::SendInput(1, &in, sizeof(INPUT));
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
//
|
||||
// Translate qt key code to windows virtual key code
|
||||
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
|
||||
//
|
||||
DWORD AutoTypePlatformWin::qtToNativeKeyCode(Qt::Key key)
|
||||
{
|
||||
switch (key) {
|
||||
case Qt::Key_Backspace:
|
||||
return VK_BACK; // 0x08
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
return VK_TAB; // 0x09
|
||||
case Qt::Key_Clear:
|
||||
return VK_CLEAR; // 0x0C
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
return VK_RETURN; // 0x0D
|
||||
case Qt::Key_Shift:
|
||||
return VK_SHIFT; // 0x10
|
||||
case Qt::Key_Control:
|
||||
return VK_CONTROL; // 0x11
|
||||
case Qt::Key_Pause:
|
||||
return VK_PAUSE; // 0x13
|
||||
case Qt::Key_CapsLock:
|
||||
return VK_CAPITAL; // 0x14
|
||||
case Qt::Key_Escape:
|
||||
return VK_ESCAPE; // 0x1B
|
||||
case Qt::Key_Space:
|
||||
return VK_SPACE; // 0x20
|
||||
case Qt::Key_PageUp:
|
||||
return VK_PRIOR; // 0x21
|
||||
case Qt::Key_PageDown:
|
||||
return VK_NEXT; // 0x22
|
||||
case Qt::Key_End:
|
||||
return VK_END; // 0x23
|
||||
case Qt::Key_Home:
|
||||
return VK_HOME; // 0x24
|
||||
case Qt::Key_Left:
|
||||
return VK_LEFT; // 0x25
|
||||
case Qt::Key_Up:
|
||||
return VK_UP; // 0x26
|
||||
case Qt::Key_Right:
|
||||
return VK_RIGHT; // 0x27
|
||||
case Qt::Key_Down:
|
||||
return VK_DOWN; // 0x28
|
||||
case Qt::Key_Print:
|
||||
return VK_SNAPSHOT; // 0x2C
|
||||
case Qt::Key_Insert:
|
||||
return VK_INSERT; // 0x2D
|
||||
case Qt::Key_Delete:
|
||||
return VK_DELETE; // 0x2E
|
||||
case Qt::Key_Help:
|
||||
return VK_HELP; // 0x2F
|
||||
|
||||
case Qt::Key_0:
|
||||
return 0x30; // 0x30
|
||||
case Qt::Key_1:
|
||||
return 0x31; // 0x31
|
||||
case Qt::Key_2:
|
||||
return 0x32; // 0x32
|
||||
case Qt::Key_3:
|
||||
return 0x33; // 0x33
|
||||
case Qt::Key_4:
|
||||
return 0x34; // 0x34
|
||||
case Qt::Key_5:
|
||||
return 0x35; // 0x35
|
||||
case Qt::Key_6:
|
||||
return 0x36; // 0x36
|
||||
case Qt::Key_7:
|
||||
return 0x37; // 0x37
|
||||
case Qt::Key_8:
|
||||
return 0x38; // 0x38
|
||||
case Qt::Key_9:
|
||||
return 0x39; // 0x39
|
||||
|
||||
case Qt::Key_A:
|
||||
return 0x41; // 0x41
|
||||
case Qt::Key_B:
|
||||
return 0x42; // 0x42
|
||||
case Qt::Key_C:
|
||||
return 0x43; // 0x43
|
||||
case Qt::Key_D:
|
||||
return 0x44; // 0x44
|
||||
case Qt::Key_E:
|
||||
return 0x45; // 0x45
|
||||
case Qt::Key_F:
|
||||
return 0x46; // 0x46
|
||||
case Qt::Key_G:
|
||||
return 0x47; // 0x47
|
||||
case Qt::Key_H:
|
||||
return 0x48; // 0x48
|
||||
case Qt::Key_I:
|
||||
return 0x49; // 0x49
|
||||
case Qt::Key_J:
|
||||
return 0x4A; // 0x4A
|
||||
case Qt::Key_K:
|
||||
return 0x4B; // 0x4B
|
||||
case Qt::Key_L:
|
||||
return 0x4C; // 0x4C
|
||||
case Qt::Key_M:
|
||||
return 0x4D; // 0x4D
|
||||
case Qt::Key_N:
|
||||
return 0x4E; // 0x4E
|
||||
case Qt::Key_O:
|
||||
return 0x4F; // 0x4F
|
||||
case Qt::Key_P:
|
||||
return 0x50; // 0x50
|
||||
case Qt::Key_Q:
|
||||
return 0x51; // 0x51
|
||||
case Qt::Key_R:
|
||||
return 0x52; // 0x52
|
||||
case Qt::Key_S:
|
||||
return 0x53; // 0x53
|
||||
case Qt::Key_T:
|
||||
return 0x54; // 0x54
|
||||
case Qt::Key_U:
|
||||
return 0x55; // 0x55
|
||||
case Qt::Key_V:
|
||||
return 0x56; // 0x56
|
||||
case Qt::Key_W:
|
||||
return 0x57; // 0x57
|
||||
case Qt::Key_X:
|
||||
return 0x58; // 0x58
|
||||
case Qt::Key_Y:
|
||||
return 0x59; // 0x59
|
||||
case Qt::Key_Z:
|
||||
return 0x5A; // 0x5A
|
||||
|
||||
case Qt::Key_F1:
|
||||
return VK_F1; // 0x70
|
||||
case Qt::Key_F2:
|
||||
return VK_F2; // 0x71
|
||||
case Qt::Key_F3:
|
||||
return VK_F3; // 0x72
|
||||
case Qt::Key_F4:
|
||||
return VK_F4; // 0x73
|
||||
case Qt::Key_F5:
|
||||
return VK_F5; // 0x74
|
||||
case Qt::Key_F6:
|
||||
return VK_F6; // 0x75
|
||||
case Qt::Key_F7:
|
||||
return VK_F7; // 0x76
|
||||
case Qt::Key_F8:
|
||||
return VK_F8; // 0x77
|
||||
case Qt::Key_F9:
|
||||
return VK_F9; // 0x78
|
||||
case Qt::Key_F10:
|
||||
return VK_F10; // 0x79
|
||||
case Qt::Key_F11:
|
||||
return VK_F11; // 0x7A
|
||||
case Qt::Key_F12:
|
||||
return VK_F12; // 0x7B
|
||||
case Qt::Key_F13:
|
||||
return VK_F13; // 0x7C
|
||||
case Qt::Key_F14:
|
||||
return VK_F14; // 0x7D
|
||||
case Qt::Key_F15:
|
||||
return VK_F15; // 0x7E
|
||||
case Qt::Key_F16:
|
||||
return VK_F16; // 0x7F
|
||||
case Qt::Key_F17:
|
||||
return VK_F17; // 0x80
|
||||
case Qt::Key_F18:
|
||||
return VK_F18; // 0x81
|
||||
case Qt::Key_F19:
|
||||
return VK_F19; // 0x82
|
||||
case Qt::Key_F20:
|
||||
return VK_F20; // 0x83
|
||||
case Qt::Key_F21:
|
||||
return VK_F21; // 0x84
|
||||
case Qt::Key_F22:
|
||||
return VK_F22; // 0x85
|
||||
case Qt::Key_F23:
|
||||
return VK_F23; // 0x86
|
||||
case Qt::Key_F24:
|
||||
return VK_F24; // 0x87
|
||||
|
||||
case Qt::Key_NumLock:
|
||||
return VK_NUMLOCK; // 0x90
|
||||
case Qt::Key_ScrollLock:
|
||||
return VK_SCROLL; // 0x91
|
||||
|
||||
case Qt::Key_Exclam: // !
|
||||
case Qt::Key_QuoteDbl: // "
|
||||
case Qt::Key_NumberSign: // #
|
||||
case Qt::Key_Dollar: // $
|
||||
case Qt::Key_Percent: // %
|
||||
case Qt::Key_Ampersand: // &
|
||||
case Qt::Key_Apostrophe: // '
|
||||
case Qt::Key_ParenLeft: // (
|
||||
case Qt::Key_ParenRight: // )
|
||||
case Qt::Key_Asterisk: // *
|
||||
case Qt::Key_Plus: // +
|
||||
case Qt::Key_Comma: // ,
|
||||
case Qt::Key_Minus: // -
|
||||
case Qt::Key_Period: // .
|
||||
case Qt::Key_Slash: // /
|
||||
case Qt::Key_Colon: // :
|
||||
case Qt::Key_Semicolon: // ;
|
||||
case Qt::Key_Less: // <
|
||||
case Qt::Key_Equal: // =
|
||||
case Qt::Key_Greater: // >
|
||||
case Qt::Key_Question: // ?
|
||||
case Qt::Key_BracketLeft: // [
|
||||
case Qt::Key_Backslash: // '\'
|
||||
case Qt::Key_BracketRight: // ]
|
||||
case Qt::Key_AsciiCircum: // ^
|
||||
case Qt::Key_Underscore: // _
|
||||
case Qt::Key_QuoteLeft: // `
|
||||
case Qt::Key_BraceLeft: // {
|
||||
case Qt::Key_Bar: // |
|
||||
case Qt::Key_BraceRight: // }
|
||||
case Qt::Key_AsciiTilde: // ~
|
||||
return LOBYTE(::VkKeyScanExW(key, ::GetKeyboardLayout(0)));
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The extended-key flag indicates whether the keystroke message originated
|
||||
// from one of the additional keys on the enhanced keyboard
|
||||
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646267%28v=vs.85%29.aspx#EXTENDED_KEY_FLAG
|
||||
//
|
||||
BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
|
||||
bool AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
|
||||
{
|
||||
switch (nativeKeyCode) {
|
||||
case VK_RMENU:
|
||||
@ -417,44 +153,21 @@ BOOL AutoTypePlatformWin::isExtendedKey(DWORD nativeKeyCode)
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
case VK_APPS:
|
||||
return TRUE;
|
||||
return true;
|
||||
default:
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
//
|
||||
// Translate qt key modifiers to windows modifiers
|
||||
//
|
||||
DWORD AutoTypePlatformWin::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
DWORD nativeModifiers = 0;
|
||||
|
||||
if (modifiers & Qt::ShiftModifier) {
|
||||
nativeModifiers |= MOD_SHIFT;
|
||||
}
|
||||
if (modifiers & Qt::ControlModifier) {
|
||||
nativeModifiers |= MOD_CONTROL;
|
||||
}
|
||||
if (modifiers & Qt::AltModifier) {
|
||||
nativeModifiers |= MOD_ALT;
|
||||
}
|
||||
if (modifiers & Qt::MetaModifier) {
|
||||
nativeModifiers |= MOD_WIN;
|
||||
}
|
||||
|
||||
return nativeModifiers;
|
||||
}
|
||||
|
||||
//
|
||||
// Test if window is in Alt+Tab list
|
||||
// see: https://blogs.msdn.microsoft.com/oldnewthing/20071008-00/?p=24863
|
||||
//
|
||||
BOOL AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
|
||||
bool AutoTypePlatformWin::isAltTabWindow(HWND hwnd)
|
||||
{
|
||||
if (!::IsWindowVisible(hwnd)) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start at the root owner
|
||||
|
@ -20,7 +20,7 @@
|
||||
#define KEEPASSX_AUTOTYPEWINDOWS_H
|
||||
|
||||
#include <QtPlugin>
|
||||
#include <Windows.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "autotype/AutoTypeAction.h"
|
||||
#include "autotype/AutoTypePlatformPlugin.h"
|
||||
@ -36,23 +36,15 @@ public:
|
||||
QStringList windowTitles() override;
|
||||
WId activeWindow() override;
|
||||
QString activeWindowTitle() override;
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
void sendChar(const QChar& ch, bool isKeyDown);
|
||||
void sendKey(Qt::Key key, bool isKeyDown);
|
||||
|
||||
signals:
|
||||
void globalShortcutTriggered();
|
||||
|
||||
private:
|
||||
static DWORD qtToNativeKeyCode(Qt::Key key);
|
||||
static DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
||||
static BOOL isExtendedKey(DWORD nativeKeyCode);
|
||||
static BOOL isAltTabWindow(HWND hwnd);
|
||||
static bool isExtendedKey(DWORD nativeKeyCode);
|
||||
static bool isAltTabWindow(HWND hwnd);
|
||||
static BOOL CALLBACK windowTitleEnumProc(_In_ HWND hwnd, _In_ LPARAM lParam);
|
||||
static QString windowTitle(HWND hwnd);
|
||||
};
|
||||
|
@ -18,15 +18,6 @@
|
||||
*/
|
||||
|
||||
#include "AutoTypeXCB.h"
|
||||
#include "KeySymMap.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
bool AutoTypePlatformX11::m_catchXErrors = false;
|
||||
bool AutoTypePlatformX11::m_xErrorOccurred = false;
|
||||
int (*AutoTypePlatformX11::m_oldXErrorHandler)(Display*, XErrorEvent*) = nullptr;
|
||||
|
||||
AutoTypePlatformX11::AutoTypePlatformX11()
|
||||
{
|
||||
@ -49,17 +40,14 @@ AutoTypePlatformX11::AutoTypePlatformX11()
|
||||
m_classBlacklist << "xfdesktop"
|
||||
<< "xfce4-panel"; // Xfce 4
|
||||
|
||||
m_currentGlobalKey = static_cast<Qt::Key>(0);
|
||||
m_currentGlobalModifiers = nullptr;
|
||||
|
||||
m_keysymTable = nullptr;
|
||||
m_xkb = nullptr;
|
||||
m_remapKeycode = 0;
|
||||
m_currentRemapKeysym = NoSymbol;
|
||||
m_modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
|
||||
|
||||
m_loaded = true;
|
||||
|
||||
connect(nixUtils(), &NixUtils::keymapChanged, this, [this] { updateKeymap(); });
|
||||
updateKeymap();
|
||||
}
|
||||
|
||||
@ -142,105 +130,6 @@ QString AutoTypePlatformX11::activeWindowTitle()
|
||||
return windowTitle(activeWindow(), true);
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
int keycode = XKeysymToKeycode(m_dpy, charToKeySym(key));
|
||||
uint nativeModifiers = qtToNativeModifiers(modifiers);
|
||||
|
||||
startCatchXErrors();
|
||||
XGrabKey(m_dpy, keycode, nativeModifiers, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(m_dpy, keycode, nativeModifiers | LockMask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask | LockMask, m_rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
stopCatchXErrors();
|
||||
|
||||
if (!m_xErrorOccurred) {
|
||||
m_currentGlobalKey = key;
|
||||
m_currentGlobalModifiers = modifiers;
|
||||
m_currentGlobalKeycode = keycode;
|
||||
m_currentGlobalNativeModifiers = nativeModifiers;
|
||||
return true;
|
||||
} else {
|
||||
unregisterGlobalShortcut(key, modifiers);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint AutoTypePlatformX11::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
uint nativeModifiers = 0;
|
||||
|
||||
if (modifiers & Qt::ShiftModifier) {
|
||||
nativeModifiers |= ShiftMask;
|
||||
}
|
||||
if (modifiers & Qt::ControlModifier) {
|
||||
nativeModifiers |= ControlMask;
|
||||
}
|
||||
if (modifiers & Qt::AltModifier) {
|
||||
nativeModifiers |= Mod1Mask;
|
||||
}
|
||||
if (modifiers & Qt::MetaModifier) {
|
||||
nativeModifiers |= Mod4Mask;
|
||||
}
|
||||
|
||||
return nativeModifiers;
|
||||
}
|
||||
|
||||
void AutoTypePlatformX11::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
KeyCode keycode = XKeysymToKeycode(m_dpy, charToKeySym(key));
|
||||
uint nativeModifiers = qtToNativeModifiers(modifiers);
|
||||
|
||||
XUngrabKey(m_dpy, keycode, nativeModifiers, m_rootWindow);
|
||||
XUngrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask, m_rootWindow);
|
||||
XUngrabKey(m_dpy, keycode, nativeModifiers | LockMask, m_rootWindow);
|
||||
XUngrabKey(m_dpy, keycode, nativeModifiers | Mod2Mask | LockMask, m_rootWindow);
|
||||
|
||||
m_currentGlobalKey = static_cast<Qt::Key>(0);
|
||||
m_currentGlobalModifiers = nullptr;
|
||||
m_currentGlobalKeycode = 0;
|
||||
m_currentGlobalNativeModifiers = 0;
|
||||
}
|
||||
|
||||
int AutoTypePlatformX11::platformEventFilter(void* event)
|
||||
{
|
||||
xcb_generic_event_t* genericEvent = static_cast<xcb_generic_event_t*>(event);
|
||||
quint8 type = genericEvent->response_type & 0x7f;
|
||||
|
||||
if (type == XCB_KEY_PRESS || type == XCB_KEY_RELEASE) {
|
||||
xcb_key_press_event_t* keyPressEvent = static_cast<xcb_key_press_event_t*>(event);
|
||||
if (keyPressEvent->detail == m_currentGlobalKeycode
|
||||
&& (keyPressEvent->state & m_modifierMask) == m_currentGlobalNativeModifiers
|
||||
&& (!QApplication::activeWindow() || QApplication::activeWindow()->isMinimized()) && m_loaded) {
|
||||
if (type == XCB_KEY_PRESS) {
|
||||
emit globalShortcutTriggered();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
} else if (type == XCB_MAPPING_NOTIFY) {
|
||||
xcb_mapping_notify_event_t* mappingNotifyEvent = static_cast<xcb_mapping_notify_event_t*>(event);
|
||||
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD
|
||||
|| mappingNotifyEvent->request == XCB_MAPPING_MODIFIER) {
|
||||
XMappingEvent xMappingEvent;
|
||||
memset(&xMappingEvent, 0, sizeof(xMappingEvent));
|
||||
xMappingEvent.type = MappingNotify;
|
||||
xMappingEvent.display = m_dpy;
|
||||
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD) {
|
||||
xMappingEvent.request = MappingKeyboard;
|
||||
} else {
|
||||
xMappingEvent.request = MappingModifier;
|
||||
}
|
||||
xMappingEvent.first_keycode = mappingNotifyEvent->first_keycode;
|
||||
xMappingEvent.count = mappingNotifyEvent->count;
|
||||
XRefreshKeyboardMapping(&xMappingEvent);
|
||||
updateKeymap();
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
AutoTypeExecutor* AutoTypePlatformX11::createExecutor()
|
||||
{
|
||||
return new AutoTypeExecutorX11(this);
|
||||
@ -395,89 +284,6 @@ bool AutoTypePlatformX11::isTopLevelWindow(Window window)
|
||||
return result;
|
||||
}
|
||||
|
||||
KeySym AutoTypePlatformX11::charToKeySym(const QChar& ch)
|
||||
{
|
||||
ushort unicode = ch.unicode();
|
||||
|
||||
/* first check for Latin-1 characters (1:1 mapping) */
|
||||
if ((unicode >= 0x0020 && unicode <= 0x007e) || (unicode >= 0x00a0 && unicode <= 0x00ff)) {
|
||||
return unicode;
|
||||
}
|
||||
|
||||
/* mapping table generated from keysymdef.h */
|
||||
const uint* match = Tools::binaryFind(m_unicodeToKeysymKeys, m_unicodeToKeysymKeys + m_unicodeToKeysymLen, unicode);
|
||||
int index = match - m_unicodeToKeysymKeys;
|
||||
if (index != m_unicodeToKeysymLen) {
|
||||
return m_unicodeToKeysymValues[index];
|
||||
}
|
||||
|
||||
if (unicode >= 0x0100) {
|
||||
return unicode | 0x01000000;
|
||||
}
|
||||
|
||||
return NoSymbol;
|
||||
}
|
||||
|
||||
KeySym AutoTypePlatformX11::keyToKeySym(Qt::Key key)
|
||||
{
|
||||
switch (key) {
|
||||
case Qt::Key_Tab:
|
||||
return XK_Tab;
|
||||
case Qt::Key_Enter:
|
||||
return XK_Return;
|
||||
case Qt::Key_Space:
|
||||
return XK_space;
|
||||
case Qt::Key_Up:
|
||||
return XK_Up;
|
||||
case Qt::Key_Down:
|
||||
return XK_Down;
|
||||
case Qt::Key_Left:
|
||||
return XK_Left;
|
||||
case Qt::Key_Right:
|
||||
return XK_Right;
|
||||
case Qt::Key_Insert:
|
||||
return XK_Insert;
|
||||
case Qt::Key_Delete:
|
||||
return XK_Delete;
|
||||
case Qt::Key_Home:
|
||||
return XK_Home;
|
||||
case Qt::Key_End:
|
||||
return XK_End;
|
||||
case Qt::Key_PageUp:
|
||||
return XK_Page_Up;
|
||||
case Qt::Key_PageDown:
|
||||
return XK_Page_Down;
|
||||
case Qt::Key_Backspace:
|
||||
return XK_BackSpace;
|
||||
case Qt::Key_Pause:
|
||||
return XK_Break;
|
||||
case Qt::Key_CapsLock:
|
||||
return XK_Caps_Lock;
|
||||
case Qt::Key_Escape:
|
||||
return XK_Escape;
|
||||
case Qt::Key_Help:
|
||||
return XK_Help;
|
||||
case Qt::Key_NumLock:
|
||||
return XK_Num_Lock;
|
||||
case Qt::Key_Print:
|
||||
return XK_Print;
|
||||
case Qt::Key_ScrollLock:
|
||||
return XK_Scroll_Lock;
|
||||
case Qt::Key_Shift:
|
||||
return XK_Shift_L;
|
||||
case Qt::Key_Control:
|
||||
return XK_Control_L;
|
||||
case Qt::Key_Alt:
|
||||
return XK_Alt_L;
|
||||
default:
|
||||
if (key >= Qt::Key_F1 && key <= Qt::Key_F16) {
|
||||
return XK_F1 + (key - Qt::Key_F1);
|
||||
} else {
|
||||
return NoSymbol;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the keyboard and modifier mapping.
|
||||
* We need the KeyboardMapping for AddKeysym.
|
||||
@ -491,8 +297,9 @@ void AutoTypePlatformX11::updateKeymap()
|
||||
m_xkb = getKeyboard();
|
||||
|
||||
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
|
||||
if (m_keysymTable != nullptr)
|
||||
if (m_keysymTable != nullptr) {
|
||||
XFree(m_keysymTable);
|
||||
}
|
||||
m_keysymTable = XGetKeyboardMapping(m_dpy, m_minKeycode, m_maxKeycode - m_minKeycode + 1, &m_keysymPerKeycode);
|
||||
|
||||
/* determine the keycode to use for remapped keys */
|
||||
@ -523,11 +330,7 @@ void AutoTypePlatformX11::updateKeymap()
|
||||
|
||||
/* Xlib needs some time until the mapping is distributed to
|
||||
all clients */
|
||||
// TODO: we should probably only sleep while in the middle of typing something
|
||||
timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 30 * 1000 * 1000;
|
||||
nanosleep(&ts, nullptr);
|
||||
Tools::sleep(30);
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::isRemapKeycodeValid()
|
||||
@ -542,36 +345,6 @@ bool AutoTypePlatformX11::isRemapKeycodeValid()
|
||||
return false;
|
||||
}
|
||||
|
||||
void AutoTypePlatformX11::startCatchXErrors()
|
||||
{
|
||||
Q_ASSERT(!m_catchXErrors);
|
||||
|
||||
m_catchXErrors = true;
|
||||
m_xErrorOccurred = false;
|
||||
m_oldXErrorHandler = XSetErrorHandler(x11ErrorHandler);
|
||||
}
|
||||
|
||||
void AutoTypePlatformX11::stopCatchXErrors()
|
||||
{
|
||||
Q_ASSERT(m_catchXErrors);
|
||||
|
||||
XSync(m_dpy, False);
|
||||
XSetErrorHandler(m_oldXErrorHandler);
|
||||
m_catchXErrors = false;
|
||||
}
|
||||
|
||||
int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error)
|
||||
{
|
||||
Q_UNUSED(display)
|
||||
Q_UNUSED(error)
|
||||
|
||||
if (m_catchXErrors) {
|
||||
m_xErrorOccurred = true;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
XkbDescPtr AutoTypePlatformX11::getKeyboard()
|
||||
{
|
||||
int num_devices;
|
||||
@ -696,7 +469,7 @@ bool AutoTypePlatformX11::keysymModifiers(KeySym keysym, int keycode, unsigned i
|
||||
* window to simulate keyboard. If modifiers (shift, control, etc)
|
||||
* are set ON, many events will be sent.
|
||||
*/
|
||||
void AutoTypePlatformX11::SendKey(KeySym keysym, unsigned int modifiers)
|
||||
void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
|
||||
{
|
||||
if (keysym == NoSymbol) {
|
||||
qWarning("No such key: keysym=0x%lX", keysym);
|
||||
@ -798,12 +571,12 @@ AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform)
|
||||
|
||||
void AutoTypeExecutorX11::execChar(AutoTypeChar* action)
|
||||
{
|
||||
m_platform->SendKey(m_platform->charToKeySym(action->character));
|
||||
m_platform->sendKey(qcharToNativeKeyCode(action->character));
|
||||
}
|
||||
|
||||
void AutoTypeExecutorX11::execKey(AutoTypeKey* action)
|
||||
{
|
||||
m_platform->SendKey(m_platform->keyToKeySym(action->key));
|
||||
m_platform->sendKey(qtToNativeKeyCode(action->key));
|
||||
}
|
||||
|
||||
void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||
@ -814,13 +587,13 @@ void AutoTypeExecutorX11::execClearField(AutoTypeClearField* action = nullptr)
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 25 * 1000 * 1000;
|
||||
|
||||
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_Home), static_cast<unsigned int>(ControlMask));
|
||||
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_Home), static_cast<unsigned int>(ControlMask));
|
||||
nanosleep(&ts, nullptr);
|
||||
|
||||
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_End), static_cast<unsigned int>(ControlMask | ShiftMask));
|
||||
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_End), static_cast<unsigned int>(ControlMask | ShiftMask));
|
||||
nanosleep(&ts, nullptr);
|
||||
|
||||
m_platform->SendKey(m_platform->keyToKeySym(Qt::Key_Backspace));
|
||||
m_platform->sendKey(qtToNativeKeyCode(Qt::Key_Backspace));
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
|
@ -26,12 +26,16 @@
|
||||
#include <QX11Info>
|
||||
#include <QtPlugin>
|
||||
|
||||
#include "autotype/AutoTypeAction.h"
|
||||
#include "autotype/AutoTypePlatformPlugin.h"
|
||||
#include "core/Tools.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
#include "gui/osutils/nixutils/X11Funcs.h"
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
#include "autotype/AutoTypeAction.h"
|
||||
#include "autotype/AutoTypePlatformPlugin.h"
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#define N_MOD_INDICES (Mod5MapIndex + 1)
|
||||
|
||||
@ -48,19 +52,10 @@ public:
|
||||
QStringList windowTitles() override;
|
||||
WId activeWindow() override;
|
||||
QString activeWindowTitle() override;
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) override;
|
||||
int platformEventFilter(void* event) override;
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
KeySym charToKeySym(const QChar& ch);
|
||||
KeySym keyToKeySym(Qt::Key key);
|
||||
|
||||
void SendKey(KeySym keysym, unsigned int modifiers = 0);
|
||||
|
||||
signals:
|
||||
void globalShortcutTriggered();
|
||||
void sendKey(KeySym keysym, unsigned int modifiers = 0);
|
||||
|
||||
private:
|
||||
QString windowTitle(Window window, bool useBlacklist);
|
||||
@ -68,10 +63,6 @@ private:
|
||||
QString windowClassName(Window window);
|
||||
QList<Window> widgetsToX11Windows(const QWidgetList& widgetList);
|
||||
bool isTopLevelWindow(Window window);
|
||||
uint qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
||||
void startCatchXErrors();
|
||||
void stopCatchXErrors();
|
||||
static int x11ErrorHandler(Display* display, XErrorEvent* error);
|
||||
|
||||
XkbDescPtr getKeyboard();
|
||||
void updateKeymap();
|
||||
@ -94,18 +85,6 @@ private:
|
||||
Atom m_atomUtf8String;
|
||||
Atom m_atomNetActiveWindow;
|
||||
QSet<QString> m_classBlacklist;
|
||||
Qt::Key m_currentGlobalKey;
|
||||
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
||||
uint m_currentGlobalKeycode;
|
||||
uint m_currentGlobalNativeModifiers;
|
||||
int m_modifierMask;
|
||||
static bool m_catchXErrors;
|
||||
static bool m_xErrorOccurred;
|
||||
static int (*m_oldXErrorHandler)(Display*, XErrorEvent*);
|
||||
|
||||
static const int m_unicodeToKeysymLen;
|
||||
static const uint m_unicodeToKeysymKeys[];
|
||||
static const uint m_unicodeToKeysymValues[];
|
||||
|
||||
XkbDescPtr m_xkb;
|
||||
KeySym* m_keysymTable;
|
||||
|
@ -36,10 +36,6 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
#include "gui/osutils/OSEventFilter.h"
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
@ -60,9 +56,7 @@ Application::Application(int& argc, char** argv)
|
||||
, m_alreadyRunning(false)
|
||||
, m_lockFile(nullptr)
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
, m_osEventFilter(new OSEventFilter())
|
||||
{
|
||||
installNativeEventFilter(m_osEventFilter.data());
|
||||
#else
|
||||
{
|
||||
#endif
|
||||
@ -160,6 +154,7 @@ void Application::bootstrap()
|
||||
QApplication::setFont(QApplication::font("QMessageBox"));
|
||||
#endif
|
||||
|
||||
osUtils->registerNativeEventFilter();
|
||||
MessageBox::initializeButtonDefs();
|
||||
|
||||
#ifdef Q_OS_MACOS
|
||||
|
@ -81,9 +81,6 @@ private:
|
||||
QLockFile* m_lockFile;
|
||||
QLocalServer m_lockServer;
|
||||
QString m_socketName;
|
||||
#if defined(Q_OS_WIN) || (defined(Q_OS_UNIX) && !defined(Q_OS_MACOS))
|
||||
QScopedPointer<OSEventFilter> m_osEventFilter;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define kpxcApp qobject_cast<Application*>(Application::instance())
|
||||
|
@ -60,7 +60,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
m_toggleVisibleAction = new QAction(
|
||||
icons()->icon("password-show-off"),
|
||||
tr("Toggle Password (%1)").arg(QKeySequence(modifier + Qt::Key_H).toString(QKeySequence::NativeText)),
|
||||
nullptr);
|
||||
this);
|
||||
m_toggleVisibleAction->setCheckable(true);
|
||||
m_toggleVisibleAction->setShortcut(modifier + Qt::Key_H);
|
||||
m_toggleVisibleAction->setShortcutContext(Qt::WidgetShortcut);
|
||||
@ -70,7 +70,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
m_passwordGeneratorAction = new QAction(
|
||||
icons()->icon("password-generator"),
|
||||
tr("Generate Password (%1)").arg(QKeySequence(modifier + Qt::Key_G).toString(QKeySequence::NativeText)),
|
||||
nullptr);
|
||||
this);
|
||||
m_passwordGeneratorAction->setShortcut(modifier + Qt::Key_G);
|
||||
m_passwordGeneratorAction->setShortcutContext(Qt::WidgetShortcut);
|
||||
addAction(m_passwordGeneratorAction, QLineEdit::TrailingPosition);
|
||||
@ -79,7 +79,7 @@ PasswordEdit::PasswordEdit(QWidget* parent)
|
||||
m_capslockAction =
|
||||
new QAction(icons()->icon("dialog-warning", true, StateColorPalette().color(StateColorPalette::Error)),
|
||||
tr("Warning: Caps Lock enabled!"),
|
||||
nullptr);
|
||||
this);
|
||||
addAction(m_capslockAction, QLineEdit::LeadingPosition);
|
||||
m_capslockAction->setVisible(false);
|
||||
}
|
||||
|
@ -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 void registerNativeEventFilter() = 0;
|
||||
|
||||
virtual bool registerGlobalShortcut(const QString& name,
|
||||
Qt::Key key,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QString* error = nullptr) = 0;
|
||||
virtual bool unregisterGlobalShortcut(const QString& name) = 0;
|
||||
|
||||
signals:
|
||||
void globalShortcutTriggered(const QString& name);
|
||||
|
||||
protected:
|
||||
explicit OSUtilsBase(QObject* parent = nullptr);
|
||||
virtual ~OSUtilsBase();
|
||||
|
@ -23,8 +23,10 @@
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <CoreGraphics/CGEventSource.h>
|
||||
|
||||
#define INVALID_KEYCODE 0xFFFF
|
||||
|
||||
QPointer<MacUtils> MacUtils::m_instance = nullptr;
|
||||
|
||||
@ -95,9 +97,10 @@ bool MacUtils::isDarkMode() const
|
||||
|
||||
QString MacUtils::getLaunchAgentFilename() const
|
||||
{
|
||||
auto launchAgentDir = QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
|
||||
return QFile(launchAgentDir.absoluteFilePath(
|
||||
qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist"))).fileName();
|
||||
auto launchAgentDir =
|
||||
QDir(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QStringLiteral("/../LaunchAgents"));
|
||||
return QFile(launchAgentDir.absoluteFilePath(qApp->property("KPXC_QUALIFIED_APPNAME").toString().append(".plist")))
|
||||
.fileName();
|
||||
}
|
||||
|
||||
bool MacUtils::isLaunchAtStartupEnabled() const
|
||||
@ -134,3 +137,306 @@ void MacUtils::toggleForegroundApp(bool foreground)
|
||||
{
|
||||
m_appkit->toggleForegroundApp(foreground);
|
||||
}
|
||||
|
||||
void MacUtils::registerNativeEventFilter()
|
||||
{
|
||||
EventTypeSpec eventSpec;
|
||||
eventSpec.eventClass = kEventClassKeyboard;
|
||||
eventSpec.eventKind = kEventHotKeyPressed;
|
||||
::InstallApplicationEventHandler(MacUtils::hotkeyHandler, 1, &eventSpec, this, nullptr);
|
||||
}
|
||||
|
||||
bool MacUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
||||
{
|
||||
auto keycode = qtToNativeKeyCode(key);
|
||||
auto modifierscode = qtToNativeModifiers(modifiers, false);
|
||||
if (keycode == INVALID_KEYCODE) {
|
||||
if (error) {
|
||||
*error = tr("Invalid key code");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this key combo is registered to another shortcut
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
|
||||
if (error) {
|
||||
*error = tr("Global shortcut already registered to %1").arg(i.key());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove existing registration for this name
|
||||
unregisterGlobalShortcut(name);
|
||||
|
||||
auto gs = QSharedPointer<globalShortcut>::create();
|
||||
gs->hotkeyId.signature = 'kpxc';
|
||||
gs->hotkeyId.id = m_nextShortcutId;
|
||||
gs->nativeKeyCode = keycode;
|
||||
gs->nativeModifiers = modifierscode;
|
||||
if (::RegisterEventHotKey(
|
||||
gs->nativeKeyCode, gs->nativeModifiers, gs->hotkeyId, GetApplicationEventTarget(), 0, &gs->hotkeyRef)
|
||||
!= noErr) {
|
||||
if (error) {
|
||||
*error = tr("Could not register global shortcut");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_globalShortcuts.insert(name, gs);
|
||||
++m_nextShortcutId;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacUtils::unregisterGlobalShortcut(const QString& name)
|
||||
{
|
||||
if (m_globalShortcuts.contains(name)) {
|
||||
auto gs = m_globalShortcuts.value(name);
|
||||
::UnregisterEventHotKey(gs->hotkeyRef);
|
||||
m_globalShortcuts.remove(name);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OSStatus MacUtils::hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData)
|
||||
{
|
||||
Q_UNUSED(nextHandler);
|
||||
|
||||
auto self = static_cast<MacUtils*>(userData);
|
||||
EventHotKeyID hotkeyId;
|
||||
if (::GetEventParameter(
|
||||
theEvent, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(hotkeyId), nullptr, &hotkeyId)
|
||||
== noErr) {
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(self->m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->hotkeyId.id == hotkeyId.id) {
|
||||
emit self->globalShortcutTriggered(i.key());
|
||||
return noErr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return eventNotHandledErr;
|
||||
}
|
||||
|
||||
//
|
||||
// Translate qt key code to mac os key code
|
||||
// see: HIToolbox/Events.h
|
||||
//
|
||||
uint16 MacUtils::qtToNativeKeyCode(Qt::Key key)
|
||||
{
|
||||
switch (key) {
|
||||
case Qt::Key_A:
|
||||
return kVK_ANSI_A;
|
||||
case Qt::Key_B:
|
||||
return kVK_ANSI_B;
|
||||
case Qt::Key_C:
|
||||
return kVK_ANSI_C;
|
||||
case Qt::Key_D:
|
||||
return kVK_ANSI_D;
|
||||
case Qt::Key_E:
|
||||
return kVK_ANSI_E;
|
||||
case Qt::Key_F:
|
||||
return kVK_ANSI_F;
|
||||
case Qt::Key_G:
|
||||
return kVK_ANSI_G;
|
||||
case Qt::Key_H:
|
||||
return kVK_ANSI_H;
|
||||
case Qt::Key_I:
|
||||
return kVK_ANSI_I;
|
||||
case Qt::Key_J:
|
||||
return kVK_ANSI_J;
|
||||
case Qt::Key_K:
|
||||
return kVK_ANSI_K;
|
||||
case Qt::Key_L:
|
||||
return kVK_ANSI_L;
|
||||
case Qt::Key_M:
|
||||
return kVK_ANSI_M;
|
||||
case Qt::Key_N:
|
||||
return kVK_ANSI_N;
|
||||
case Qt::Key_O:
|
||||
return kVK_ANSI_O;
|
||||
case Qt::Key_P:
|
||||
return kVK_ANSI_P;
|
||||
case Qt::Key_Q:
|
||||
return kVK_ANSI_Q;
|
||||
case Qt::Key_R:
|
||||
return kVK_ANSI_R;
|
||||
case Qt::Key_S:
|
||||
return kVK_ANSI_S;
|
||||
case Qt::Key_T:
|
||||
return kVK_ANSI_T;
|
||||
case Qt::Key_U:
|
||||
return kVK_ANSI_U;
|
||||
case Qt::Key_V:
|
||||
return kVK_ANSI_V;
|
||||
case Qt::Key_W:
|
||||
return kVK_ANSI_W;
|
||||
case Qt::Key_X:
|
||||
return kVK_ANSI_X;
|
||||
case Qt::Key_Y:
|
||||
return kVK_ANSI_Y;
|
||||
case Qt::Key_Z:
|
||||
return kVK_ANSI_Z;
|
||||
|
||||
case Qt::Key_0:
|
||||
return kVK_ANSI_0;
|
||||
case Qt::Key_1:
|
||||
return kVK_ANSI_1;
|
||||
case Qt::Key_2:
|
||||
return kVK_ANSI_2;
|
||||
case Qt::Key_3:
|
||||
return kVK_ANSI_3;
|
||||
case Qt::Key_4:
|
||||
return kVK_ANSI_4;
|
||||
case Qt::Key_5:
|
||||
return kVK_ANSI_5;
|
||||
case Qt::Key_6:
|
||||
return kVK_ANSI_6;
|
||||
case Qt::Key_7:
|
||||
return kVK_ANSI_7;
|
||||
case Qt::Key_8:
|
||||
return kVK_ANSI_8;
|
||||
case Qt::Key_9:
|
||||
return kVK_ANSI_9;
|
||||
|
||||
case Qt::Key_Equal:
|
||||
return kVK_ANSI_Equal;
|
||||
case Qt::Key_Minus:
|
||||
return kVK_ANSI_Minus;
|
||||
case Qt::Key_BracketRight:
|
||||
return kVK_ANSI_RightBracket;
|
||||
case Qt::Key_BracketLeft:
|
||||
return kVK_ANSI_LeftBracket;
|
||||
case Qt::Key_QuoteDbl:
|
||||
return kVK_ANSI_Quote;
|
||||
case Qt::Key_Semicolon:
|
||||
return kVK_ANSI_Semicolon;
|
||||
case Qt::Key_Backslash:
|
||||
return kVK_ANSI_Backslash;
|
||||
case Qt::Key_Comma:
|
||||
return kVK_ANSI_Comma;
|
||||
case Qt::Key_Slash:
|
||||
return kVK_ANSI_Slash;
|
||||
case Qt::Key_Period:
|
||||
return kVK_ANSI_Period;
|
||||
|
||||
case Qt::Key_Shift:
|
||||
return kVK_Shift;
|
||||
case Qt::Key_Control:
|
||||
return kVK_Command;
|
||||
case Qt::Key_Backspace:
|
||||
return kVK_Delete;
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
return kVK_Tab;
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
return kVK_Return;
|
||||
case Qt::Key_CapsLock:
|
||||
return kVK_CapsLock;
|
||||
case Qt::Key_Escape:
|
||||
return kVK_Escape;
|
||||
case Qt::Key_Space:
|
||||
return kVK_Space;
|
||||
case Qt::Key_PageUp:
|
||||
return kVK_PageUp;
|
||||
case Qt::Key_PageDown:
|
||||
return kVK_PageDown;
|
||||
case Qt::Key_End:
|
||||
return kVK_End;
|
||||
case Qt::Key_Home:
|
||||
return kVK_Home;
|
||||
case Qt::Key_Left:
|
||||
return kVK_LeftArrow;
|
||||
case Qt::Key_Up:
|
||||
return kVK_UpArrow;
|
||||
case Qt::Key_Right:
|
||||
return kVK_RightArrow;
|
||||
case Qt::Key_Down:
|
||||
return kVK_DownArrow;
|
||||
case Qt::Key_Delete:
|
||||
return kVK_ForwardDelete;
|
||||
case Qt::Key_Help:
|
||||
return kVK_Help;
|
||||
|
||||
case Qt::Key_F1:
|
||||
return kVK_F1;
|
||||
case Qt::Key_F2:
|
||||
return kVK_F2;
|
||||
case Qt::Key_F3:
|
||||
return kVK_F3;
|
||||
case Qt::Key_F4:
|
||||
return kVK_F4;
|
||||
case Qt::Key_F5:
|
||||
return kVK_F5;
|
||||
case Qt::Key_F6:
|
||||
return kVK_F6;
|
||||
case Qt::Key_F7:
|
||||
return kVK_F7;
|
||||
case Qt::Key_F8:
|
||||
return kVK_F8;
|
||||
case Qt::Key_F9:
|
||||
return kVK_F9;
|
||||
case Qt::Key_F10:
|
||||
return kVK_F10;
|
||||
case Qt::Key_F11:
|
||||
return kVK_F11;
|
||||
case Qt::Key_F12:
|
||||
return kVK_F12;
|
||||
case Qt::Key_F13:
|
||||
return kVK_F13;
|
||||
case Qt::Key_F14:
|
||||
return kVK_F14;
|
||||
case Qt::Key_F15:
|
||||
return kVK_F15;
|
||||
case Qt::Key_F16:
|
||||
return kVK_F16;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return INVALID_KEYCODE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Translate qt key modifiers to mac os modifiers
|
||||
// see: https://doc.qt.io/qt-5/osx-issues.html#special-keys
|
||||
//
|
||||
CGEventFlags MacUtils::qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native)
|
||||
{
|
||||
CGEventFlags nativeModifiers = CGEventFlags(0);
|
||||
|
||||
CGEventFlags shiftMod = CGEventFlags(shiftKey);
|
||||
CGEventFlags cmdMod = CGEventFlags(cmdKey);
|
||||
CGEventFlags optionMod = CGEventFlags(optionKey);
|
||||
CGEventFlags controlMod = CGEventFlags(controlKey);
|
||||
|
||||
if (native) {
|
||||
shiftMod = kCGEventFlagMaskShift;
|
||||
cmdMod = kCGEventFlagMaskCommand;
|
||||
optionMod = kCGEventFlagMaskAlternate;
|
||||
controlMod = kCGEventFlagMaskControl;
|
||||
}
|
||||
|
||||
if (modifiers & Qt::ShiftModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | shiftMod);
|
||||
}
|
||||
if (modifiers & Qt::ControlModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | cmdMod);
|
||||
}
|
||||
if (modifiers & Qt::AltModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | optionMod);
|
||||
}
|
||||
if (modifiers & Qt::MetaModifier) {
|
||||
nativeModifiers = CGEventFlags(nativeModifiers | controlMod);
|
||||
}
|
||||
|
||||
return nativeModifiers;
|
||||
}
|
||||
|
@ -19,8 +19,9 @@
|
||||
#ifndef KEEPASSXC_MACUTILS_H
|
||||
#define KEEPASSXC_MACUTILS_H
|
||||
|
||||
#include "gui/osutils/OSUtilsBase.h"
|
||||
#include "AppKit.h"
|
||||
#include "gui/osutils/OSUtilsBase.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include <QPointer>
|
||||
#include <QScopedPointer>
|
||||
@ -48,6 +49,17 @@ public:
|
||||
bool enableScreenRecording();
|
||||
void toggleForegroundApp(bool foreground);
|
||||
|
||||
void registerNativeEventFilter() override;
|
||||
|
||||
bool registerGlobalShortcut(const QString& name,
|
||||
Qt::Key key,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QString* error = nullptr) override;
|
||||
bool unregisterGlobalShortcut(const QString& name) override;
|
||||
|
||||
uint16 qtToNativeKeyCode(Qt::Key key);
|
||||
CGEventFlags qtToNativeModifiers(Qt::KeyboardModifiers modifiers, bool native);
|
||||
|
||||
signals:
|
||||
void lockDatabases();
|
||||
|
||||
@ -57,10 +69,22 @@ protected:
|
||||
|
||||
private:
|
||||
QString getLaunchAgentFilename() const;
|
||||
static OSStatus hotkeyHandler(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData);
|
||||
|
||||
QScopedPointer<AppKit> m_appkit;
|
||||
static QPointer<MacUtils> m_instance;
|
||||
|
||||
struct globalShortcut
|
||||
{
|
||||
EventHotKeyRef hotkeyRef;
|
||||
EventHotKeyID hotkeyId;
|
||||
uint16 nativeKeyCode;
|
||||
CGEventFlags nativeModifiers;
|
||||
};
|
||||
|
||||
int m_nextShortcutId = 1;
|
||||
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
|
||||
|
||||
Q_DISABLE_COPY(MacUtils)
|
||||
};
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
* Automatically generated by keysymmap.py from parsing keysymdef.h.
|
||||
*/
|
||||
|
||||
const int AutoTypePlatformX11::m_unicodeToKeysymLen = 632;
|
||||
const int unicodeToKeysymLen = 632;
|
||||
|
||||
// clang-format off
|
||||
const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {
|
||||
const uint unicodeToKeysymKeys[] = {
|
||||
0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
|
||||
0x0108, 0x0109, 0x010a, 0x010b, 0x010c, 0x010d, 0x010e, 0x010f,
|
||||
0x0110, 0x0111, 0x0112, 0x0113, 0x0116, 0x0117, 0x0118, 0x0119,
|
||||
@ -87,7 +87,7 @@ const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {
|
||||
0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f2, 0x30f3, 0x30fb, 0x30fc
|
||||
};
|
||||
|
||||
const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {
|
||||
const uint unicodeToKeysymValues[] = {
|
||||
0x03c0, 0x03e0, 0x01c3, 0x01e3, 0x01a1, 0x01b1, 0x01c6, 0x01e6,
|
||||
0x02c6, 0x02e6, 0x02c5, 0x02e5, 0x01c8, 0x01e8, 0x01cf, 0x01ef,
|
||||
0x01d0, 0x01f0, 0x03aa, 0x03ba, 0x03cc, 0x03ec, 0x01ca, 0x01ea,
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
|
||||
#include "NixUtils.h"
|
||||
#include "KeySymMap.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QColor>
|
||||
@ -26,13 +28,26 @@
|
||||
#include <QStandardPaths>
|
||||
#include <QStyle>
|
||||
#include <QTextStream>
|
||||
#include <QtX11Extras/QX11Info>
|
||||
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
// namespace required to avoid name clashes with declarations in XKBlib.h
|
||||
namespace X11
|
||||
{
|
||||
|
||||
#include "X11Funcs.h"
|
||||
#include <X11/XKBlib.h>
|
||||
}
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
Display* dpy;
|
||||
Window rootWindow;
|
||||
bool x11ErrorOccurred = false;
|
||||
|
||||
int x11ErrorHandler(Display*, XErrorEvent*)
|
||||
{
|
||||
x11ErrorOccurred = true;
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
QPointer<NixUtils> NixUtils::m_instance = nullptr;
|
||||
|
||||
@ -48,6 +63,8 @@ NixUtils* NixUtils::instance()
|
||||
NixUtils::NixUtils(QObject* parent)
|
||||
: OSUtilsBase(parent)
|
||||
{
|
||||
dpy = QX11Info::display();
|
||||
rootWindow = QX11Info::appRootWindow();
|
||||
}
|
||||
|
||||
NixUtils::~NixUtils()
|
||||
@ -125,7 +142,7 @@ bool NixUtils::isCapslockEnabled()
|
||||
QString platform = QGuiApplication::platformName();
|
||||
if (platform == "xcb") {
|
||||
unsigned state = 0;
|
||||
if (X11::XkbGetIndicatorState(reinterpret_cast<X11::Display*>(display), XkbUseCoreKbd, &state) == Success) {
|
||||
if (XkbGetIndicatorState(reinterpret_cast<Display*>(display), XkbUseCoreKbd, &state) == Success) {
|
||||
return ((state & 1u) != 0);
|
||||
}
|
||||
}
|
||||
@ -134,3 +151,119 @@ bool NixUtils::isCapslockEnabled()
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NixUtils::registerNativeEventFilter()
|
||||
{
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
bool NixUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
|
||||
{
|
||||
if (eventType != QByteArrayLiteral("xcb_generic_event_t")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* genericEvent = static_cast<xcb_generic_event_t*>(message);
|
||||
quint8 type = genericEvent->response_type & 0x7f;
|
||||
|
||||
if (type == XCB_KEY_PRESS) {
|
||||
auto* keyPressEvent = static_cast<xcb_key_press_event_t*>(message);
|
||||
auto modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
|
||||
return triggerGlobalShortcut(keyPressEvent->detail, keyPressEvent->state & modifierMask);
|
||||
} else if (type == XCB_MAPPING_NOTIFY) {
|
||||
auto* mappingNotifyEvent = static_cast<xcb_mapping_notify_event_t*>(message);
|
||||
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD
|
||||
|| mappingNotifyEvent->request == XCB_MAPPING_MODIFIER) {
|
||||
XMappingEvent xMappingEvent;
|
||||
memset(&xMappingEvent, 0, sizeof(xMappingEvent));
|
||||
xMappingEvent.type = MappingNotify;
|
||||
xMappingEvent.display = dpy;
|
||||
if (mappingNotifyEvent->request == XCB_MAPPING_KEYBOARD) {
|
||||
xMappingEvent.request = MappingKeyboard;
|
||||
} else {
|
||||
xMappingEvent.request = MappingModifier;
|
||||
}
|
||||
xMappingEvent.first_keycode = mappingNotifyEvent->first_keycode;
|
||||
xMappingEvent.count = mappingNotifyEvent->count;
|
||||
XRefreshKeyboardMapping(&xMappingEvent);
|
||||
// Notify listeners that the keymap has changed
|
||||
emit keymapChanged();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NixUtils::triggerGlobalShortcut(uint keycode, uint modifiers)
|
||||
{
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifiers) {
|
||||
emit globalShortcutTriggered(i.key());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NixUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
||||
{
|
||||
auto keycode = XKeysymToKeycode(dpy, qcharToNativeKeyCode(key));
|
||||
auto modifierscode = qtToNativeModifiers(modifiers);
|
||||
|
||||
// Check if this key combo is registered to another shortcut
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
|
||||
if (error) {
|
||||
*error = tr("Global shortcut already registered to %1").arg(i.key());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unregisterGlobalShortcut(name);
|
||||
|
||||
x11ErrorOccurred = false;
|
||||
auto prevHandler = XSetErrorHandler(x11ErrorHandler);
|
||||
|
||||
XGrabKey(dpy, keycode, modifierscode, rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(dpy, keycode, modifierscode | Mod2Mask, rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(dpy, keycode, modifierscode | LockMask, rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
XGrabKey(dpy, keycode, modifierscode | Mod2Mask | LockMask, rootWindow, True, GrabModeAsync, GrabModeAsync);
|
||||
|
||||
XSync(dpy, False);
|
||||
XSetErrorHandler(prevHandler);
|
||||
|
||||
if (x11ErrorOccurred) {
|
||||
x11ErrorOccurred = false;
|
||||
if (error) {
|
||||
*error = tr("Could not register global shortcut");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto gs = QSharedPointer<globalShortcut>::create();
|
||||
gs->nativeKeyCode = keycode;
|
||||
gs->nativeModifiers = modifierscode;
|
||||
m_globalShortcuts.insert(name, gs);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NixUtils::unregisterGlobalShortcut(const QString& name)
|
||||
{
|
||||
if (!m_globalShortcuts.contains(name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto gs = m_globalShortcuts.value(name);
|
||||
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers, rootWindow);
|
||||
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | Mod2Mask, rootWindow);
|
||||
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | LockMask, rootWindow);
|
||||
XUngrabKey(dpy, gs->nativeKeyCode, gs->nativeModifiers | Mod2Mask | LockMask, rootWindow);
|
||||
|
||||
m_globalShortcuts.remove(name);
|
||||
return true;
|
||||
}
|
||||
|
@ -19,9 +19,10 @@
|
||||
#define KEEPASSXC_NIXUTILS_H
|
||||
|
||||
#include "gui/osutils/OSUtilsBase.h"
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QPointer>
|
||||
|
||||
class NixUtils : public OSUtilsBase
|
||||
class NixUtils : public OSUtilsBase, QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -33,15 +34,35 @@ public:
|
||||
void setLaunchAtStartup(bool enable) override;
|
||||
bool isCapslockEnabled() override;
|
||||
|
||||
void registerNativeEventFilter() override;
|
||||
|
||||
bool registerGlobalShortcut(const QString& name,
|
||||
Qt::Key key,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QString* error = nullptr) override;
|
||||
bool unregisterGlobalShortcut(const QString& name) override;
|
||||
|
||||
signals:
|
||||
void keymapChanged();
|
||||
|
||||
private:
|
||||
explicit NixUtils(QObject* parent = nullptr);
|
||||
~NixUtils() override;
|
||||
|
||||
private:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
||||
QString getAutostartDesktopFilename(bool createDirs = false) const;
|
||||
|
||||
bool triggerGlobalShortcut(uint keycode, uint modifiers);
|
||||
|
||||
static QPointer<NixUtils> m_instance;
|
||||
|
||||
struct globalShortcut
|
||||
{
|
||||
uint nativeKeyCode;
|
||||
uint nativeModifiers;
|
||||
};
|
||||
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
|
||||
|
||||
Q_DISABLE_COPY(NixUtils)
|
||||
};
|
||||
|
||||
|
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("const uint AutoTypePlatformX11::m_unicodeToKeysymKeys[] = {")
|
||||
print("const uint unicodeToKeysymKeys[] = {")
|
||||
keys = keysymMap.keys()
|
||||
keyLen = len(keys)
|
||||
for idx, val in enumerate(keys, start=1):
|
||||
@ -84,7 +84,7 @@ print("};")
|
||||
|
||||
print()
|
||||
|
||||
print("const uint AutoTypePlatformX11::m_unicodeToKeysymValues[] = {")
|
||||
print("const uint unicodeToKeysymValues[] = {")
|
||||
values = keysymMap.values()
|
||||
valuesLen = len(values)
|
||||
for idx, val in enumerate(values, start=1):
|
@ -24,7 +24,6 @@
|
||||
#include <windows.h>
|
||||
|
||||
QPointer<WinUtils> WinUtils::m_instance = nullptr;
|
||||
QScopedPointer<WinUtils::DWMEventFilter> WinUtils::m_eventFilter;
|
||||
|
||||
WinUtils* WinUtils::instance()
|
||||
{
|
||||
@ -48,39 +47,34 @@ WinUtils::WinUtils(QObject* parent)
|
||||
{
|
||||
}
|
||||
|
||||
WinUtils::~WinUtils()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register event filters to handle native platform events such as theme changes.
|
||||
* Register event filters to handle native platform events such as global hotkeys
|
||||
*/
|
||||
void WinUtils::registerEventFilters()
|
||||
void WinUtils::registerNativeEventFilter()
|
||||
{
|
||||
if (!m_eventFilter) {
|
||||
m_eventFilter.reset(new DWMEventFilter);
|
||||
qApp->installNativeEventFilter(m_eventFilter.data());
|
||||
}
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
bool WinUtils::DWMEventFilter::nativeEventFilter(const QByteArray& eventType, void* message, long*)
|
||||
bool WinUtils::nativeEventFilter(const QByteArray& eventType, void* message, long*)
|
||||
{
|
||||
if (eventType != "windows_generic_MSG") {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* msg = static_cast<MSG*>(message);
|
||||
if (!msg->hwnd) {
|
||||
return false;
|
||||
}
|
||||
switch (msg->message) {
|
||||
/* TODO: indicate dark mode support for black title bar
|
||||
case WM_CREATE:
|
||||
case WM_INITDIALOG: {
|
||||
if (winUtils()->isDarkMode()) {
|
||||
// TODO: indicate dark mode support for black title bar
|
||||
if (msg->hwnd && winUtils()->isDarkMode()) {
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
*/
|
||||
case WM_HOTKEY:
|
||||
triggerGlobalShortcut(msg->wParam);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -119,3 +113,319 @@ bool WinUtils::isHighContrastMode() const
|
||||
QSettings settings(R"(HKEY_CURRENT_USER\Control Panel\Accessibility\HighContrast)", QSettings::NativeFormat);
|
||||
return (settings.value("Flags").toInt() & 1u) != 0;
|
||||
}
|
||||
|
||||
bool WinUtils::registerGlobalShortcut(const QString& name, Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error)
|
||||
{
|
||||
auto keycode = qtToNativeKeyCode(key);
|
||||
auto modifierscode = qtToNativeModifiers(modifiers);
|
||||
if (keycode < 1 || keycode > 254) {
|
||||
if (error) {
|
||||
*error = tr("Invalid key code");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if this key combo is registered to another shortcut
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->nativeKeyCode == keycode && i.value()->nativeModifiers == modifierscode && i.key() != name) {
|
||||
if (error) {
|
||||
*error = tr("Global shortcut already registered to %1").arg(i.key());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unregisterGlobalShortcut(name);
|
||||
|
||||
auto gs = QSharedPointer<globalShortcut>::create();
|
||||
gs->id = m_nextShortcutId;
|
||||
gs->nativeKeyCode = keycode;
|
||||
gs->nativeModifiers = modifierscode;
|
||||
if (!::RegisterHotKey(nullptr, gs->id, gs->nativeModifiers | MOD_NOREPEAT, gs->nativeKeyCode)) {
|
||||
if (error) {
|
||||
*error = tr("Could not register global shortcut");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
m_globalShortcuts.insert(name, gs);
|
||||
|
||||
if (++m_nextShortcutId > 0xBFFF) {
|
||||
// Roll over if greater than the max id per
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey#remarks
|
||||
m_nextShortcutId = 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WinUtils::unregisterGlobalShortcut(const QString& name)
|
||||
{
|
||||
if (m_globalShortcuts.contains(name)) {
|
||||
auto gs = m_globalShortcuts.value(name);
|
||||
if (::UnregisterHotKey(nullptr, gs->id)) {
|
||||
m_globalShortcuts.remove(name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WinUtils::triggerGlobalShortcut(int id)
|
||||
{
|
||||
QHashIterator<QString, QSharedPointer<globalShortcut>> i(m_globalShortcuts);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
if (i.value()->id == id) {
|
||||
emit globalShortcutTriggered(i.key());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
//
|
||||
// Translate qt key code to windows virtual key code
|
||||
// see: https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx
|
||||
//
|
||||
DWORD WinUtils::qtToNativeKeyCode(Qt::Key key)
|
||||
{
|
||||
switch (key) {
|
||||
case Qt::Key_Backspace:
|
||||
return VK_BACK; // 0x08
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
return VK_TAB; // 0x09
|
||||
case Qt::Key_Clear:
|
||||
return VK_CLEAR; // 0x0C
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
return VK_RETURN; // 0x0D
|
||||
case Qt::Key_Shift:
|
||||
return VK_SHIFT; // 0x10
|
||||
case Qt::Key_Control:
|
||||
return VK_CONTROL; // 0x11
|
||||
case Qt::Key_Pause:
|
||||
return VK_PAUSE; // 0x13
|
||||
case Qt::Key_CapsLock:
|
||||
return VK_CAPITAL; // 0x14
|
||||
case Qt::Key_Escape:
|
||||
return VK_ESCAPE; // 0x1B
|
||||
case Qt::Key_Space:
|
||||
return VK_SPACE; // 0x20
|
||||
case Qt::Key_PageUp:
|
||||
return VK_PRIOR; // 0x21
|
||||
case Qt::Key_PageDown:
|
||||
return VK_NEXT; // 0x22
|
||||
case Qt::Key_End:
|
||||
return VK_END; // 0x23
|
||||
case Qt::Key_Home:
|
||||
return VK_HOME; // 0x24
|
||||
case Qt::Key_Left:
|
||||
return VK_LEFT; // 0x25
|
||||
case Qt::Key_Up:
|
||||
return VK_UP; // 0x26
|
||||
case Qt::Key_Right:
|
||||
return VK_RIGHT; // 0x27
|
||||
case Qt::Key_Down:
|
||||
return VK_DOWN; // 0x28
|
||||
case Qt::Key_Print:
|
||||
return VK_SNAPSHOT; // 0x2C
|
||||
case Qt::Key_Insert:
|
||||
return VK_INSERT; // 0x2D
|
||||
case Qt::Key_Delete:
|
||||
return VK_DELETE; // 0x2E
|
||||
case Qt::Key_Help:
|
||||
return VK_HELP; // 0x2F
|
||||
|
||||
case Qt::Key_0:
|
||||
return 0x30; // 0x30
|
||||
case Qt::Key_1:
|
||||
return 0x31; // 0x31
|
||||
case Qt::Key_2:
|
||||
return 0x32; // 0x32
|
||||
case Qt::Key_3:
|
||||
return 0x33; // 0x33
|
||||
case Qt::Key_4:
|
||||
return 0x34; // 0x34
|
||||
case Qt::Key_5:
|
||||
return 0x35; // 0x35
|
||||
case Qt::Key_6:
|
||||
return 0x36; // 0x36
|
||||
case Qt::Key_7:
|
||||
return 0x37; // 0x37
|
||||
case Qt::Key_8:
|
||||
return 0x38; // 0x38
|
||||
case Qt::Key_9:
|
||||
return 0x39; // 0x39
|
||||
|
||||
case Qt::Key_A:
|
||||
return 0x41; // 0x41
|
||||
case Qt::Key_B:
|
||||
return 0x42; // 0x42
|
||||
case Qt::Key_C:
|
||||
return 0x43; // 0x43
|
||||
case Qt::Key_D:
|
||||
return 0x44; // 0x44
|
||||
case Qt::Key_E:
|
||||
return 0x45; // 0x45
|
||||
case Qt::Key_F:
|
||||
return 0x46; // 0x46
|
||||
case Qt::Key_G:
|
||||
return 0x47; // 0x47
|
||||
case Qt::Key_H:
|
||||
return 0x48; // 0x48
|
||||
case Qt::Key_I:
|
||||
return 0x49; // 0x49
|
||||
case Qt::Key_J:
|
||||
return 0x4A; // 0x4A
|
||||
case Qt::Key_K:
|
||||
return 0x4B; // 0x4B
|
||||
case Qt::Key_L:
|
||||
return 0x4C; // 0x4C
|
||||
case Qt::Key_M:
|
||||
return 0x4D; // 0x4D
|
||||
case Qt::Key_N:
|
||||
return 0x4E; // 0x4E
|
||||
case Qt::Key_O:
|
||||
return 0x4F; // 0x4F
|
||||
case Qt::Key_P:
|
||||
return 0x50; // 0x50
|
||||
case Qt::Key_Q:
|
||||
return 0x51; // 0x51
|
||||
case Qt::Key_R:
|
||||
return 0x52; // 0x52
|
||||
case Qt::Key_S:
|
||||
return 0x53; // 0x53
|
||||
case Qt::Key_T:
|
||||
return 0x54; // 0x54
|
||||
case Qt::Key_U:
|
||||
return 0x55; // 0x55
|
||||
case Qt::Key_V:
|
||||
return 0x56; // 0x56
|
||||
case Qt::Key_W:
|
||||
return 0x57; // 0x57
|
||||
case Qt::Key_X:
|
||||
return 0x58; // 0x58
|
||||
case Qt::Key_Y:
|
||||
return 0x59; // 0x59
|
||||
case Qt::Key_Z:
|
||||
return 0x5A; // 0x5A
|
||||
|
||||
case Qt::Key_F1:
|
||||
return VK_F1; // 0x70
|
||||
case Qt::Key_F2:
|
||||
return VK_F2; // 0x71
|
||||
case Qt::Key_F3:
|
||||
return VK_F3; // 0x72
|
||||
case Qt::Key_F4:
|
||||
return VK_F4; // 0x73
|
||||
case Qt::Key_F5:
|
||||
return VK_F5; // 0x74
|
||||
case Qt::Key_F6:
|
||||
return VK_F6; // 0x75
|
||||
case Qt::Key_F7:
|
||||
return VK_F7; // 0x76
|
||||
case Qt::Key_F8:
|
||||
return VK_F8; // 0x77
|
||||
case Qt::Key_F9:
|
||||
return VK_F9; // 0x78
|
||||
case Qt::Key_F10:
|
||||
return VK_F10; // 0x79
|
||||
case Qt::Key_F11:
|
||||
return VK_F11; // 0x7A
|
||||
case Qt::Key_F12:
|
||||
return VK_F12; // 0x7B
|
||||
case Qt::Key_F13:
|
||||
return VK_F13; // 0x7C
|
||||
case Qt::Key_F14:
|
||||
return VK_F14; // 0x7D
|
||||
case Qt::Key_F15:
|
||||
return VK_F15; // 0x7E
|
||||
case Qt::Key_F16:
|
||||
return VK_F16; // 0x7F
|
||||
case Qt::Key_F17:
|
||||
return VK_F17; // 0x80
|
||||
case Qt::Key_F18:
|
||||
return VK_F18; // 0x81
|
||||
case Qt::Key_F19:
|
||||
return VK_F19; // 0x82
|
||||
case Qt::Key_F20:
|
||||
return VK_F20; // 0x83
|
||||
case Qt::Key_F21:
|
||||
return VK_F21; // 0x84
|
||||
case Qt::Key_F22:
|
||||
return VK_F22; // 0x85
|
||||
case Qt::Key_F23:
|
||||
return VK_F23; // 0x86
|
||||
case Qt::Key_F24:
|
||||
return VK_F24; // 0x87
|
||||
|
||||
case Qt::Key_NumLock:
|
||||
return VK_NUMLOCK; // 0x90
|
||||
case Qt::Key_ScrollLock:
|
||||
return VK_SCROLL; // 0x91
|
||||
|
||||
case Qt::Key_Exclam: // !
|
||||
case Qt::Key_QuoteDbl: // "
|
||||
case Qt::Key_NumberSign: // #
|
||||
case Qt::Key_Dollar: // $
|
||||
case Qt::Key_Percent: // %
|
||||
case Qt::Key_Ampersand: // &
|
||||
case Qt::Key_Apostrophe: // '
|
||||
case Qt::Key_ParenLeft: // (
|
||||
case Qt::Key_ParenRight: // )
|
||||
case Qt::Key_Asterisk: // *
|
||||
case Qt::Key_Plus: // +
|
||||
case Qt::Key_Comma: // ,
|
||||
case Qt::Key_Minus: // -
|
||||
case Qt::Key_Period: // .
|
||||
case Qt::Key_Slash: // /
|
||||
case Qt::Key_Colon: // :
|
||||
case Qt::Key_Semicolon: // ;
|
||||
case Qt::Key_Less: // <
|
||||
case Qt::Key_Equal: // =
|
||||
case Qt::Key_Greater: // >
|
||||
case Qt::Key_Question: // ?
|
||||
case Qt::Key_BracketLeft: // [
|
||||
case Qt::Key_Backslash: // '\'
|
||||
case Qt::Key_BracketRight: // ]
|
||||
case Qt::Key_AsciiCircum: // ^
|
||||
case Qt::Key_Underscore: // _
|
||||
case Qt::Key_QuoteLeft: // `
|
||||
case Qt::Key_BraceLeft: // {
|
||||
case Qt::Key_Bar: // |
|
||||
case Qt::Key_BraceRight: // }
|
||||
case Qt::Key_AsciiTilde: // ~
|
||||
return LOBYTE(::VkKeyScanExW(key, ::GetKeyboardLayout(0)));
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
//
|
||||
// Translate qt key modifiers to windows modifiers
|
||||
//
|
||||
DWORD WinUtils::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
|
||||
{
|
||||
DWORD nativeModifiers = 0;
|
||||
|
||||
if (modifiers & Qt::ShiftModifier) {
|
||||
nativeModifiers |= MOD_SHIFT;
|
||||
}
|
||||
if (modifiers & Qt::ControlModifier) {
|
||||
nativeModifiers |= MOD_CONTROL;
|
||||
}
|
||||
if (modifiers & Qt::AltModifier) {
|
||||
nativeModifiers |= MOD_ALT;
|
||||
}
|
||||
if (modifiers & Qt::MetaModifier) {
|
||||
nativeModifiers |= MOD_WIN;
|
||||
}
|
||||
|
||||
return nativeModifiers;
|
||||
}
|
||||
|
@ -23,14 +23,16 @@
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QPointer>
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
|
||||
class WinUtils : public OSUtilsBase
|
||||
#include <windef.h>
|
||||
|
||||
class WinUtils : public OSUtilsBase, QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static WinUtils* instance();
|
||||
static void registerEventFilters();
|
||||
|
||||
bool isDarkMode() const override;
|
||||
bool isLaunchAtStartupEnabled() const override;
|
||||
@ -38,19 +40,36 @@ public:
|
||||
bool isCapslockEnabled() override;
|
||||
bool isHighContrastMode() const;
|
||||
|
||||
void registerNativeEventFilter() override;
|
||||
|
||||
bool registerGlobalShortcut(const QString& name,
|
||||
Qt::Key key,
|
||||
Qt::KeyboardModifiers modifiers,
|
||||
QString* error = nullptr) override;
|
||||
bool unregisterGlobalShortcut(const QString& name) override;
|
||||
|
||||
DWORD qtToNativeKeyCode(Qt::Key key);
|
||||
DWORD qtToNativeModifiers(Qt::KeyboardModifiers modifiers);
|
||||
|
||||
protected:
|
||||
explicit WinUtils(QObject* parent = nullptr);
|
||||
~WinUtils() override;
|
||||
~WinUtils() override = default;
|
||||
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
||||
void triggerGlobalShortcut(int id);
|
||||
|
||||
private:
|
||||
class DWMEventFilter : public QAbstractNativeEventFilter
|
||||
static QPointer<WinUtils> m_instance;
|
||||
|
||||
struct globalShortcut
|
||||
{
|
||||
public:
|
||||
bool nativeEventFilter(const QByteArray& eventType, void* message, long*) override;
|
||||
int id;
|
||||
DWORD nativeKeyCode;
|
||||
DWORD nativeModifiers;
|
||||
};
|
||||
|
||||
static QPointer<WinUtils> m_instance;
|
||||
static QScopedPointer<DWMEventFilter> m_eventFilter;
|
||||
int m_nextShortcutId = 1;
|
||||
QHash<QString, QSharedPointer<globalShortcut>> m_globalShortcuts;
|
||||
|
||||
Q_DISABLE_COPY(WinUtils)
|
||||
};
|
||||
|
@ -119,8 +119,6 @@ void DarkStyle::polish(QWidget* widget)
|
||||
palette.setColor(QPalette::Disabled, QPalette::Window, QRgb(0x252525));
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
// Register event filter for better dark mode support
|
||||
WinUtils::registerEventFilters();
|
||||
palette.setColor(QPalette::All, QPalette::Window, QRgb(0x2F2F30));
|
||||
#else
|
||||
palette.setColor(QPalette::Active, QPalette::Window, QRgb(0x2F2F30));
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "core/Resources.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "gui/osutils/OSUtils.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestAutoType)
|
||||
|
||||
@ -157,7 +158,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch()
|
||||
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
||||
{
|
||||
m_test->setActiveWindowTitle("custom window");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
|
||||
@ -168,7 +169,7 @@ void TestAutoType::testGlobalAutoTypeTitleMatch()
|
||||
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||
|
||||
m_test->setActiveWindowTitle("An Entry Title!");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||
@ -179,7 +180,7 @@ void TestAutoType::testGlobalAutoTypeUrlMatch()
|
||||
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||
|
||||
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||
@ -190,7 +191,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
|
||||
config()->set(Config::AutoTypeEntryTitleMatch, true);
|
||||
|
||||
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||
@ -199,7 +200,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
|
||||
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
|
||||
{
|
||||
m_test->setActiveWindowTitle("An Entry Title!");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
MessageBox::setNextAnswer(MessageBox::Ok);
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
|
||||
@ -210,68 +211,68 @@ void TestAutoType::testGlobalAutoTypeRegExp()
|
||||
{
|
||||
// substring matches are ok
|
||||
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||
m_test->clearActions();
|
||||
|
||||
// should be case-insensitive
|
||||
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||
m_test->clearActions();
|
||||
|
||||
// exact match
|
||||
m_test->setActiveWindowTitle("REGEX2");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
||||
m_test->clearActions();
|
||||
|
||||
// a bit more complicated regex
|
||||
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
||||
m_test->clearActions();
|
||||
|
||||
// with custom attributes
|
||||
m_test->setActiveWindowTitle("CustomAttr1");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
|
||||
m_test->clearActions();
|
||||
|
||||
// with (non uppercase) undefined custom attributes
|
||||
m_test->setActiveWindowTitle("CustomAttr2");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString(""));
|
||||
m_test->clearActions();
|
||||
|
||||
// with mixedcase default attributes
|
||||
m_test->setActiveWindowTitle("CustomAttr3");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
|
||||
m_test->clearActions();
|
||||
|
||||
// with resolve placeholders in window association title
|
||||
m_test->setActiveWindowTitle("AttrValueFirst");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
|
||||
m_test->clearActions();
|
||||
|
||||
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
|
||||
m_test->clearActions();
|
||||
|
||||
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
|
||||
m_test->triggerGlobalAutoType();
|
||||
emit osUtils->globalShortcutTriggered("autotype");
|
||||
m_autoType->performGlobalAutoType(m_dbList);
|
||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
|
||||
m_test->clearActions();
|
||||
|
@ -70,13 +70,13 @@ void TestCli::initTestCase()
|
||||
Config::createTempFileInstance();
|
||||
Bootstrap::bootstrap();
|
||||
|
||||
auto fd = new QFile();
|
||||
m_devNull.reset(new QFile());
|
||||
#ifdef Q_OS_WIN
|
||||
fd->open(fopen("nul", "w"), QIODevice::WriteOnly);
|
||||
m_devNull->open(fopen("nul", "w"), QIODevice::WriteOnly);
|
||||
#else
|
||||
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
||||
m_devNull->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
||||
#endif
|
||||
Utils::DEVNULL.setDevice(fd);
|
||||
Utils::DEVNULL.setDevice(m_devNull.data());
|
||||
}
|
||||
|
||||
void TestCli::init()
|
||||
@ -131,6 +131,11 @@ void TestCli::cleanup()
|
||||
Utils::STDIN.setDevice(nullptr);
|
||||
}
|
||||
|
||||
void TestCli::cleanupTestCase()
|
||||
{
|
||||
m_devNull.reset();
|
||||
}
|
||||
|
||||
QSharedPointer<Database> TestCli::readDatabase(const QString& filename, const QString& pw, const QString& keyfile)
|
||||
{
|
||||
auto db = QSharedPointer<Database>::create();
|
||||
@ -520,8 +525,7 @@ void TestCli::testClip()
|
||||
|
||||
setInput("a");
|
||||
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "-a", "TESTAttribute1", "/Sample Entry"});
|
||||
QVERIFY(m_stderr->readAll().contains(
|
||||
"ERROR: attribute TESTAttribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
|
||||
QVERIFY(m_stderr->readAll().contains("ERROR: attribute TESTAttribute1 is ambiguous"));
|
||||
|
||||
setInput("a");
|
||||
execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry"});
|
||||
@ -1776,8 +1780,7 @@ void TestCli::testShow()
|
||||
setInput("a");
|
||||
execCmd(showCmd, {"show", m_dbFile->fileName(), "-a", "Testattribute1", "/Sample Entry"});
|
||||
QCOMPARE(m_stdout->readAll(), QByteArray());
|
||||
QVERIFY(m_stderr->readAll().contains(
|
||||
"ERROR: attribute Testattribute1 is ambiguous, it matches TestAttribute1 and testattribute1.\n"));
|
||||
QVERIFY(m_stderr->readAll().contains("ERROR: attribute Testattribute1 is ambiguous"));
|
||||
}
|
||||
|
||||
void TestCli::testInvalidDbFiles()
|
||||
|
@ -45,6 +45,7 @@ private slots:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testBatchCommands();
|
||||
void testAdd();
|
||||
@ -81,6 +82,7 @@ private slots:
|
||||
void testYubiKeyOption();
|
||||
|
||||
private:
|
||||
QScopedPointer<QFile> m_devNull;
|
||||
QScopedPointer<TemporaryFile> m_dbFile;
|
||||
QScopedPointer<TemporaryFile> m_dbFile2;
|
||||
QScopedPointer<TemporaryFile> m_dbFileMulti;
|
||||
|
Loading…
x
Reference in New Issue
Block a user