mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-03 01:50:10 -05:00
Add initial auto-type implementation.
The platform dependent bits are separated in plugins. A plugin for X11 using Xlib is already done.
This commit is contained in:
parent
073f3f9dfc
commit
bc207714da
@ -89,6 +89,8 @@ endif()
|
|||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--no-add-needed -Wl,--as-needed -Wl,--no-undefined")
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro")
|
||||||
|
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")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WITH_LTO)
|
if(WITH_LTO)
|
||||||
|
1
COPYING
1
COPYING
@ -24,6 +24,7 @@ Copyright: 2010-2012, Felix Geyer <debfx@fobos.de>
|
|||||||
2007, Trolltech ASA <info@trolltech.com>
|
2007, Trolltech ASA <info@trolltech.com>
|
||||||
2012, Intel Corporation
|
2012, Intel Corporation
|
||||||
2012, Nokia Corporation and/or its subsidiary(-ies)
|
2012, Nokia Corporation and/or its subsidiary(-ies)
|
||||||
|
2000-2008, Tom Sato <VEF00200@nifty.ne.jp>
|
||||||
License: GPL-2 or GPL-3
|
License: GPL-2 or GPL-3
|
||||||
|
|
||||||
Files: share/icons/database/*.png
|
Files: share/icons/database/*.png
|
||||||
|
@ -18,6 +18,9 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
|||||||
configure_file(config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h)
|
configure_file(config-keepassx.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-keepassx.h)
|
||||||
|
|
||||||
set(keepassx_SOURCES
|
set(keepassx_SOURCES
|
||||||
|
autotype/AutoType.cpp
|
||||||
|
autotype/AutoTypeAction.cpp
|
||||||
|
autotype/ShortcutWidget.cpp
|
||||||
core/Config.cpp
|
core/Config.cpp
|
||||||
core/Database.cpp
|
core/Database.cpp
|
||||||
core/DatabaseIcons.cpp
|
core/DatabaseIcons.cpp
|
||||||
@ -92,6 +95,8 @@ set(keepassx_SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
set(keepassx_MOC
|
set(keepassx_MOC
|
||||||
|
autotype/AutoType.h
|
||||||
|
autotype/ShortcutWidget.h
|
||||||
core/Config.h
|
core/Config.h
|
||||||
core/Database.h
|
core/Database.h
|
||||||
core/Entry.h
|
core/Entry.h
|
||||||
@ -181,6 +186,8 @@ install(TARGETS ${PROGNAME}
|
|||||||
BUNDLE DESTINATION . COMPONENT Runtime
|
BUNDLE DESTINATION . COMPONENT Runtime
|
||||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime)
|
RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime)
|
||||||
|
|
||||||
|
add_subdirectory(autotype)
|
||||||
|
|
||||||
if(APPLE AND NOT (${CMAKE_VERSION} VERSION_LESS 2.8.8))
|
if(APPLE AND NOT (${CMAKE_VERSION} VERSION_LESS 2.8.8))
|
||||||
if(QT_MAC_USE_COCOA AND EXISTS "${QT_LIBRARY_DIR}/Resources/qt_menu.nib")
|
if(QT_MAC_USE_COCOA AND EXISTS "${QT_LIBRARY_DIR}/Resources/qt_menu.nib")
|
||||||
install(DIRECTORY "${QT_LIBRARY_DIR}/Resources/qt_menu.nib"
|
install(DIRECTORY "${QT_LIBRARY_DIR}/Resources/qt_menu.nib"
|
||||||
|
406
src/autotype/AutoType.cpp
Normal file
406
src/autotype/AutoType.cpp
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 "AutoType.h"
|
||||||
|
|
||||||
|
#include <QtCore/QPluginLoader>
|
||||||
|
#include <QtGui/QApplication>
|
||||||
|
|
||||||
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "core/Entry.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "core/ListDeleter.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
|
|
||||||
|
AutoType* AutoType::m_instance = Q_NULLPTR;
|
||||||
|
|
||||||
|
AutoType::AutoType(QObject* parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_inAutoType(false)
|
||||||
|
, m_pluginLoader(new QPluginLoader(this))
|
||||||
|
, m_plugin(Q_NULLPTR)
|
||||||
|
, m_executor(Q_NULLPTR)
|
||||||
|
{
|
||||||
|
// prevent crash when the plugin has unresolved symbols
|
||||||
|
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
|
||||||
|
|
||||||
|
// TODO: scan in proper paths
|
||||||
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
|
m_pluginLoader->setFileName(QCoreApplication::applicationDirPath() + "/autotype/x11/libkeepassx-autotype-x11.so");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QObject* pluginInstance = m_pluginLoader->instance();
|
||||||
|
if (pluginInstance) {
|
||||||
|
m_plugin = qobject_cast<AutoTypePlatformInterface*>(pluginInstance);
|
||||||
|
if (m_plugin) {
|
||||||
|
m_executor = m_plugin->createExecutor();
|
||||||
|
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_plugin) {
|
||||||
|
qWarning("Unable to load auto-type plugin:\n%s", qPrintable(m_pluginLoader->errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoType::~AutoType()
|
||||||
|
{
|
||||||
|
delete m_executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoType* AutoType::instance()
|
||||||
|
{
|
||||||
|
if (!m_instance) {
|
||||||
|
m_instance = new AutoType(qApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AutoType::windowTitles()
|
||||||
|
{
|
||||||
|
if (!m_plugin) {
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_plugin->windowTitles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QString& customSequence)
|
||||||
|
{
|
||||||
|
if (m_inAutoType || !m_plugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_inAutoType = true;
|
||||||
|
|
||||||
|
QString sequence;
|
||||||
|
if (customSequence.isEmpty()) {
|
||||||
|
sequence = entry->resolvePlaceholders(entry->autoTypeSequence());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sequence = customSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<AutoTypeAction*> actions;
|
||||||
|
ListDeleter<AutoTypeAction*> actionsDeleter(&actions);
|
||||||
|
|
||||||
|
if (!parseActions(sequence, entry, actions)) {
|
||||||
|
m_inAutoType = false; // TODO: make this automatic
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideWindow) {
|
||||||
|
hideWindow->showMinimized();
|
||||||
|
}
|
||||||
|
|
||||||
|
Tools::wait(500);
|
||||||
|
|
||||||
|
WId activeWindow = m_plugin->activeWindow();
|
||||||
|
|
||||||
|
Q_FOREACH (AutoTypeAction* action, actions) {
|
||||||
|
if (m_plugin->activeWindow() != activeWindow) {
|
||||||
|
qWarning("Active window changed, interrupting auto-type.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
action->accept(m_executor);
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_inAutoType = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoType::performGlobalAutoType(const QList<Database*> dbList)
|
||||||
|
{
|
||||||
|
if (m_inAutoType || !m_plugin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString windowTitle = m_plugin->activeWindowTitle();
|
||||||
|
|
||||||
|
if (windowTitle.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_inAutoType = true;
|
||||||
|
|
||||||
|
QList<QPair<Entry*, QString> > entryList;
|
||||||
|
|
||||||
|
Q_FOREACH (Database* db, dbList) {
|
||||||
|
Q_FOREACH (Entry* entry, db->rootGroup()->entriesRecursive()) {
|
||||||
|
QString sequence = entry->autoTypeSequence(windowTitle);
|
||||||
|
if (!sequence.isEmpty()) {
|
||||||
|
entryList << QPair<Entry*, QString>(entry, sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entryList.isEmpty()) {
|
||||||
|
m_inAutoType = false;
|
||||||
|
}
|
||||||
|
else if (entryList.size() == 1) {
|
||||||
|
m_inAutoType = false;
|
||||||
|
performAutoType(entryList.first().first, 0, entryList.first().second);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO: implement
|
||||||
|
m_inAutoType = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions)
|
||||||
|
{
|
||||||
|
QString tmpl;
|
||||||
|
bool inTmpl = false;
|
||||||
|
|
||||||
|
Q_FOREACH (const QChar& ch, sequence) {
|
||||||
|
// TODO: implement support for {{}, {}} and {DELAY=X}
|
||||||
|
|
||||||
|
if (inTmpl) {
|
||||||
|
if (ch == '{') {
|
||||||
|
qWarning("Syntax error in auto-type sequence.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (ch == '}') {
|
||||||
|
actions.append(createActionFromTemplate(tmpl, entry));
|
||||||
|
inTmpl = false;
|
||||||
|
tmpl.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tmpl += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ch == '{') {
|
||||||
|
inTmpl = true;
|
||||||
|
}
|
||||||
|
else if (ch == '}') {
|
||||||
|
qWarning("Syntax error in auto-type sequence.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
actions.append(new AutoTypeChar(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry)
|
||||||
|
{
|
||||||
|
QString tmplName = tmpl.toLower();
|
||||||
|
int num = -1;
|
||||||
|
QList<AutoTypeAction*> list;
|
||||||
|
|
||||||
|
QRegExp repeatRegEx("(.+) (\\d+)", Qt::CaseSensitive, QRegExp::RegExp2);
|
||||||
|
if (repeatRegEx.exactMatch(tmplName)) {
|
||||||
|
tmplName = repeatRegEx.cap(1);
|
||||||
|
num = repeatRegEx.cap(2).toInt();
|
||||||
|
|
||||||
|
if (num == 0) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
// some safety checks
|
||||||
|
else if (tmplName == "delay") {
|
||||||
|
if (num > 10000) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (num > 100) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmplName == "tab") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Tab));
|
||||||
|
}
|
||||||
|
else if (tmplName == "enter") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Enter));
|
||||||
|
}
|
||||||
|
else if (tmplName == "up") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Up));
|
||||||
|
}
|
||||||
|
else if (tmplName == "down") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Down));
|
||||||
|
}
|
||||||
|
else if (tmplName == "left") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Left));
|
||||||
|
}
|
||||||
|
else if (tmplName == "right") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Right));
|
||||||
|
}
|
||||||
|
else if (tmplName == "insert" || tmplName == "ins") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Insert));
|
||||||
|
}
|
||||||
|
else if (tmplName == "delete" || tmplName == "del") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Delete));
|
||||||
|
}
|
||||||
|
else if (tmplName == "home") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Home));
|
||||||
|
}
|
||||||
|
else if (tmplName == "end") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_End));
|
||||||
|
}
|
||||||
|
else if (tmplName == "pgup") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_PageUp));
|
||||||
|
}
|
||||||
|
else if (tmplName == "pgdown") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_PageDown));
|
||||||
|
}
|
||||||
|
else if (tmplName == "backspace" || tmplName == "bs" || tmplName == "bksp") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Backspace));
|
||||||
|
}
|
||||||
|
else if (tmplName == "break") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Pause));
|
||||||
|
}
|
||||||
|
else if (tmplName == "capslock") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_CapsLock));
|
||||||
|
}
|
||||||
|
else if (tmplName == "esc") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Escape));
|
||||||
|
}
|
||||||
|
else if (tmplName == "help") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Help));
|
||||||
|
}
|
||||||
|
else if (tmplName == "numlock") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_NumLock));
|
||||||
|
}
|
||||||
|
else if (tmplName == "ptrsc") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_Print));
|
||||||
|
}
|
||||||
|
else if (tmplName == "scolllock") {
|
||||||
|
list.append(new AutoTypeKey(Qt::Key_ScrollLock));
|
||||||
|
}
|
||||||
|
// Qt doesn't know about keypad keys so use the normal ones instead
|
||||||
|
else if (tmplName == "add" || tmplName == "+") {
|
||||||
|
list.append(new AutoTypeChar('+'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "subtract") {
|
||||||
|
list.append(new AutoTypeChar('-'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "multiply") {
|
||||||
|
list.append(new AutoTypeChar('*'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "divide") {
|
||||||
|
list.append(new AutoTypeChar('/'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "^") {
|
||||||
|
list.append(new AutoTypeChar('^'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "%") {
|
||||||
|
list.append(new AutoTypeChar('%'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "~") {
|
||||||
|
list.append(new AutoTypeChar('~'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "(") {
|
||||||
|
list.append(new AutoTypeChar('('));
|
||||||
|
}
|
||||||
|
else if (tmplName == ")") {
|
||||||
|
list.append(new AutoTypeChar(')'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "{") {
|
||||||
|
list.append(new AutoTypeChar('{'));
|
||||||
|
}
|
||||||
|
else if (tmplName == "}") {
|
||||||
|
list.append(new AutoTypeChar('}'));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QRegExp fnRegexp("f(\\d+)", Qt::CaseInsensitive, QRegExp::RegExp2);
|
||||||
|
if (fnRegexp.exactMatch(tmplName)) {
|
||||||
|
int fnNo = fnRegexp.cap(1).toInt();
|
||||||
|
if (fnNo >= 1 && fnNo <= 16) {
|
||||||
|
list.append(new AutoTypeKey(static_cast<Qt::Key>(Qt::Key_F1 - 1 + fnNo)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
for (int i = 1; i < num; i++) {
|
||||||
|
list.append(list.at(0)->clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (tmplName == "delay" && num > 0) {
|
||||||
|
list.append(new AutoTypeDelay(num));
|
||||||
|
}
|
||||||
|
else if (tmplName == "clearfield") {
|
||||||
|
list.append(new AutoTypeClearField());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString placeholder = QString("{%1}").arg(tmplName);
|
||||||
|
QString resolved = entry->resolvePlaceholders(placeholder);
|
||||||
|
if (placeholder != resolved) {
|
||||||
|
Q_FOREACH (const QChar& ch, resolved) {
|
||||||
|
list.append(new AutoTypeChar(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
76
src/autotype/AutoType.h
Normal file
76
src/autotype/AutoType.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 KEEPASSX_AUTOTYPE_H
|
||||||
|
#define KEEPASSX_AUTOTYPE_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
|
class AutoTypeAction;
|
||||||
|
class AutoTypeExecutor;
|
||||||
|
class AutoTypePlatformInterface;
|
||||||
|
class Database;
|
||||||
|
class Entry;
|
||||||
|
class QPluginLoader;
|
||||||
|
|
||||||
|
class AutoType : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QStringList windowTitles();
|
||||||
|
void performAutoType(const Entry* entry, QWidget* hideWindow = Q_NULLPTR,
|
||||||
|
const QString& customSequence = QString());
|
||||||
|
void performGlobalAutoType(const QList<Database*> dbList);
|
||||||
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
|
void unregisterGlobalShortcut();
|
||||||
|
int callEventFilter(void* event);
|
||||||
|
|
||||||
|
inline bool isAvailable() {
|
||||||
|
return m_plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AutoType* instance();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void globalShortcutTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit AutoType(QObject* parent = Q_NULLPTR);
|
||||||
|
~AutoType();
|
||||||
|
bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
|
||||||
|
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
|
||||||
|
|
||||||
|
bool m_inAutoType;
|
||||||
|
Qt::Key m_currentGlobalKey;
|
||||||
|
Qt::KeyboardModifiers m_currentGlobalModifiers;
|
||||||
|
QPluginLoader* m_pluginLoader;
|
||||||
|
AutoTypePlatformInterface* m_plugin;
|
||||||
|
AutoTypeExecutor* m_executor;
|
||||||
|
static AutoType* m_instance;
|
||||||
|
|
||||||
|
Q_DISABLE_COPY(AutoType)
|
||||||
|
};
|
||||||
|
|
||||||
|
inline AutoType* autoType() {
|
||||||
|
return AutoType::instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPE_H
|
93
src/autotype/AutoTypeAction.cpp
Normal file
93
src/autotype/AutoTypeAction.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 "AutoTypeAction.h"
|
||||||
|
|
||||||
|
#include "core/Tools.h"
|
||||||
|
|
||||||
|
AutoTypeChar::AutoTypeChar(const QChar& character)
|
||||||
|
: character(character)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeAction* AutoTypeChar::clone()
|
||||||
|
{
|
||||||
|
return new AutoTypeChar(character);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeChar::accept(AutoTypeExecutor* executor)
|
||||||
|
{
|
||||||
|
executor->execChar(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AutoTypeKey::AutoTypeKey(Qt::Key key)
|
||||||
|
: key(key)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeAction* AutoTypeKey::clone()
|
||||||
|
{
|
||||||
|
return new AutoTypeKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeKey::accept(AutoTypeExecutor* executor)
|
||||||
|
{
|
||||||
|
executor->execKey(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AutoTypeDelay::AutoTypeDelay(int delayMs)
|
||||||
|
: delayMs(delayMs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeAction* AutoTypeDelay::clone()
|
||||||
|
{
|
||||||
|
return new AutoTypeDelay(delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeDelay::accept(AutoTypeExecutor* executor)
|
||||||
|
{
|
||||||
|
executor->execDelay(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AutoTypeClearField::AutoTypeClearField()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeAction* AutoTypeClearField::clone()
|
||||||
|
{
|
||||||
|
return new AutoTypeClearField();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeClearField::accept(AutoTypeExecutor* executor)
|
||||||
|
{
|
||||||
|
executor->execClearField(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AutoTypeExecutor::execDelay(AutoTypeDelay* action)
|
||||||
|
{
|
||||||
|
Tools::wait(action->delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecutor::execClearField(AutoTypeClearField* action)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
}
|
82
src/autotype/AutoTypeAction.h
Normal file
82
src/autotype/AutoTypeAction.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 KEEPASSX_AUTOTYPEACTION_H
|
||||||
|
#define KEEPASSX_AUTOTYPEACTION_H
|
||||||
|
|
||||||
|
#include <QtCore/QChar>
|
||||||
|
#include <QtCore/Qt>
|
||||||
|
|
||||||
|
class AutoTypeExecutor;
|
||||||
|
|
||||||
|
class AutoTypeAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AutoTypeAction() {}
|
||||||
|
virtual AutoTypeAction* clone() = 0;
|
||||||
|
virtual void accept(AutoTypeExecutor* executor) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeChar : public AutoTypeAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeChar(const QChar& character);
|
||||||
|
AutoTypeAction* clone();
|
||||||
|
void accept(AutoTypeExecutor* executor);
|
||||||
|
|
||||||
|
const QChar character;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeKey : public AutoTypeAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeKey(Qt::Key key);
|
||||||
|
AutoTypeAction* clone();
|
||||||
|
void accept(AutoTypeExecutor* executor);
|
||||||
|
|
||||||
|
const Qt::Key key;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeDelay : public AutoTypeAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeDelay(int delayMs);
|
||||||
|
AutoTypeAction* clone();
|
||||||
|
void accept(AutoTypeExecutor* executor);
|
||||||
|
|
||||||
|
const int delayMs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeClearField : public AutoTypeAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeClearField();
|
||||||
|
AutoTypeAction* clone();
|
||||||
|
void accept(AutoTypeExecutor* executor);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeExecutor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AutoTypeExecutor() {}
|
||||||
|
virtual void execChar(AutoTypeChar* action) = 0;
|
||||||
|
virtual void execKey(AutoTypeKey* action) = 0;
|
||||||
|
virtual void execDelay(AutoTypeDelay* action);
|
||||||
|
virtual void execClearField(AutoTypeClearField* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEACTION_H
|
43
src/autotype/AutoTypePlatformPlugin.h
Normal file
43
src/autotype/AutoTypePlatformPlugin.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 KEEPASSX_AUTOTYPEPLATFORMPLUGIN_H
|
||||||
|
#define KEEPASSX_AUTOTYPEPLATFORMPLUGIN_H
|
||||||
|
|
||||||
|
#include <QtGui/QWidget>
|
||||||
|
|
||||||
|
#include "autotype/AutoTypeAction.h"
|
||||||
|
|
||||||
|
class AutoTypePlatformInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~AutoTypePlatformInterface() {}
|
||||||
|
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 AutoTypeExecutor* createExecutor() = 0;
|
||||||
|
|
||||||
|
// implementations should also provide a globalShortcutTriggered() signal
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_INTERFACE(AutoTypePlatformInterface, "org.keepassx.AutoTypePlatformInterface/1")
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEPLATFORMPLUGIN_H
|
7
src/autotype/CMakeLists.txt
Normal file
7
src/autotype/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
if(Q_WS_X11)
|
||||||
|
find_package(X11)
|
||||||
|
|
||||||
|
if(X11_FOUND AND X11_XTest_FOUND)
|
||||||
|
add_subdirectory(x11)
|
||||||
|
endif()
|
||||||
|
endif()
|
128
src/autotype/ShortcutWidget.cpp
Normal file
128
src/autotype/ShortcutWidget.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 "ShortcutWidget.h"
|
||||||
|
|
||||||
|
#include <QtGui/QKeyEvent>
|
||||||
|
|
||||||
|
#include "autotype/AutoType.h"
|
||||||
|
|
||||||
|
ShortcutWidget::ShortcutWidget(QWidget* parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
, m_key(static_cast<Qt::Key>(0))
|
||||||
|
, m_modifiers(0)
|
||||||
|
, m_locked(false)
|
||||||
|
{
|
||||||
|
setReadOnly(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Key ShortcutWidget::key() const
|
||||||
|
{
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::KeyboardModifiers ShortcutWidget::modifiers() const
|
||||||
|
{
|
||||||
|
return m_modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutWidget::setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
m_key = key;
|
||||||
|
m_modifiers = modifiers;
|
||||||
|
m_locked = true;
|
||||||
|
|
||||||
|
displayShortcut(m_key, m_modifiers);
|
||||||
|
|
||||||
|
if (autoType()->registerGlobalShortcut(m_key, m_modifiers)) {
|
||||||
|
setStyleSheet("");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setStyleSheet("background-color: #FF9696;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutWidget::keyPressEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
keyEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutWidget::keyReleaseEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
keyEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutWidget::keyEvent(QKeyEvent* event)
|
||||||
|
{
|
||||||
|
event->accept();
|
||||||
|
|
||||||
|
if (event->type() != QEvent::KeyPress && event->type() != QEvent::KeyRelease) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool release = (event->type() == QEvent::KeyRelease);
|
||||||
|
|
||||||
|
if (m_locked && release) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::Key key = static_cast<Qt::Key>(event->key());
|
||||||
|
|
||||||
|
if (key <= 0 || key == Qt::Key_unknown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::KeyboardModifiers modifiers = event->modifiers() & (Qt::SHIFT | Qt::CTRL | Qt::ALT | Qt::META);
|
||||||
|
|
||||||
|
bool keyIsModifier;
|
||||||
|
switch (key) {
|
||||||
|
case Qt::Key_Shift:
|
||||||
|
case Qt::Key_Control:
|
||||||
|
case Qt::Key_Meta:
|
||||||
|
case Qt::Key_Alt:
|
||||||
|
case Qt::Key_AltGr:
|
||||||
|
keyIsModifier = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
keyIsModifier = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!release && !keyIsModifier) {
|
||||||
|
if (modifiers != 0) {
|
||||||
|
setShortcut(key, modifiers);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_locked = false;
|
||||||
|
setStyleSheet("");
|
||||||
|
displayShortcut(key, modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (m_locked) {
|
||||||
|
m_locked = false;
|
||||||
|
m_key = static_cast<Qt::Key>(0);
|
||||||
|
setStyleSheet("");
|
||||||
|
}
|
||||||
|
|
||||||
|
displayShortcut(static_cast<Qt::Key>(0), modifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShortcutWidget::displayShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
setText(QKeySequence(key | modifiers).toString(QKeySequence::NativeText));
|
||||||
|
}
|
48
src/autotype/ShortcutWidget.h
Normal file
48
src/autotype/ShortcutWidget.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
*
|
||||||
|
* 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 KEEPASSX_SHORTCUTWIDGET_H
|
||||||
|
#define KEEPASSX_SHORTCUTWIDGET_H
|
||||||
|
|
||||||
|
#include <QtGui/QLineEdit>
|
||||||
|
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
|
class ShortcutWidget : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ShortcutWidget(QWidget* parent = Q_NULLPTR);
|
||||||
|
Qt::Key key() const;
|
||||||
|
Qt::KeyboardModifiers modifiers() const;
|
||||||
|
void setShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void keyPressEvent(QKeyEvent* event) Q_DECL_OVERRIDE;
|
||||||
|
void keyReleaseEvent(QKeyEvent* event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void keyEvent(QKeyEvent* event);
|
||||||
|
void displayShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
|
|
||||||
|
Qt::Key m_key;
|
||||||
|
Qt::KeyboardModifiers m_modifiers;
|
||||||
|
bool m_locked;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_SHORTCUTWIDGET_H
|
773
src/autotype/x11/AutoTypeX11.cpp
Normal file
773
src/autotype/x11/AutoTypeX11.cpp
Normal file
@ -0,0 +1,773 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2000-2008 Tom Sato <VEF00200@nifty.ne.jp>
|
||||||
|
*
|
||||||
|
* 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 "AutoTypeX11.h"
|
||||||
|
|
||||||
|
bool AutoTypePlatformX11::m_catchXErrors = false;
|
||||||
|
bool AutoTypePlatformX11::m_xErrorOccured = false;
|
||||||
|
int (*AutoTypePlatformX11::m_oldXErrorHandler) (Display*, XErrorEvent*) = Q_NULLPTR;
|
||||||
|
|
||||||
|
AutoTypePlatformX11::AutoTypePlatformX11()
|
||||||
|
{
|
||||||
|
m_dpy = QX11Info::display();
|
||||||
|
m_rootWindow = QX11Info::appRootWindow();
|
||||||
|
|
||||||
|
m_atomWmState = XInternAtom(m_dpy, "WM_STATE", true);
|
||||||
|
m_atomWmName = XInternAtom(m_dpy, "_NET_WM_NAME", true);
|
||||||
|
m_atomUtf8String = XInternAtom(m_dpy, "UTF8_STRING", true);
|
||||||
|
|
||||||
|
m_classBlacklist << "desktop_window" << "gnome-panel"; // Gnome
|
||||||
|
m_classBlacklist << "kdesktop" << "kicker"; // KDE 3
|
||||||
|
m_classBlacklist << "Plasma"; // KDE 4
|
||||||
|
m_classBlacklist << "xfdesktop" << "xfce4-panel"; // Xfce 4
|
||||||
|
|
||||||
|
m_currentGlobalKey = static_cast<Qt::Key>(0);
|
||||||
|
m_currentGlobalModifiers = 0;
|
||||||
|
|
||||||
|
m_keysymTable = Q_NULLPTR;
|
||||||
|
m_altMask = 0;
|
||||||
|
m_metaMask = 0;
|
||||||
|
m_altgrMask = 0;
|
||||||
|
m_altgrKeysym = NoSymbol;
|
||||||
|
|
||||||
|
updateKeymap();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AutoTypePlatformX11::windowTitles()
|
||||||
|
{
|
||||||
|
return windowTitlesRecursive(m_rootWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
WId AutoTypePlatformX11::activeWindow()
|
||||||
|
{
|
||||||
|
Window window;
|
||||||
|
int revert_to_return;
|
||||||
|
XGetInputFocus(m_dpy, &window, &revert_to_return);
|
||||||
|
|
||||||
|
int tree;
|
||||||
|
do {
|
||||||
|
if (isTopLevelWindow(window)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window root;
|
||||||
|
Window parent;
|
||||||
|
Window* children = Q_NULLPTR;
|
||||||
|
unsigned int numChildren;
|
||||||
|
tree = XQueryTree(m_dpy, window, &root, &parent, &children, &numChildren);
|
||||||
|
window = parent;
|
||||||
|
if (children) {
|
||||||
|
XFree(children);
|
||||||
|
}
|
||||||
|
} while (tree && window);
|
||||||
|
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_xErrorOccured) {
|
||||||
|
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 |= m_altMask;
|
||||||
|
}
|
||||||
|
if (modifiers & Qt::MetaModifier) {
|
||||||
|
nativeModifiers |= m_metaMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nativeModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypePlatformX11::unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
|
||||||
|
{
|
||||||
|
KeyCode keycode = XKeysymToKeycode(m_dpy, keyToKeySym(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 = 0;
|
||||||
|
m_currentGlobalKeycode = 0;
|
||||||
|
m_currentGlobalNativeModifiers = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypePlatformX11::platformEventFilter(void* event)
|
||||||
|
{
|
||||||
|
XEvent* xevent = static_cast<XEvent*>(event);
|
||||||
|
|
||||||
|
if (xevent->type == KeyPress && m_currentGlobalKey && xevent->xkey.keycode == m_currentGlobalKeycode
|
||||||
|
&& (xevent->xkey.state & m_modifierMask) == m_currentGlobalNativeModifiers
|
||||||
|
&& !QApplication::focusWidget()) {
|
||||||
|
QMetaObject::invokeMethod(this, "globalShortcutTriggered", Qt::QueuedConnection);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (xevent->type == MappingNotify) {
|
||||||
|
updateKeymap();
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoTypeExecutor* AutoTypePlatformX11::createExecutor()
|
||||||
|
{
|
||||||
|
return new AutoTypeExecturorX11(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AutoTypePlatformX11::windowTitle(Window window, bool useBlacklist)
|
||||||
|
{
|
||||||
|
QString title;
|
||||||
|
|
||||||
|
Atom type;
|
||||||
|
int format;
|
||||||
|
unsigned long nitems;
|
||||||
|
unsigned long after;
|
||||||
|
unsigned char* data = Q_NULLPTR;
|
||||||
|
|
||||||
|
int retVal = XGetWindowProperty(m_dpy, window, m_atomWmName, 0, 1000, false, m_atomUtf8String,
|
||||||
|
&type, &format, &nitems, &after, &data);
|
||||||
|
|
||||||
|
if (retVal != 0) {
|
||||||
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (data) {
|
||||||
|
title = QString::fromUtf8(reinterpret_cast<char*>(data));
|
||||||
|
XFree(data);
|
||||||
|
|
||||||
|
if (useBlacklist) {
|
||||||
|
if (window == m_rootWindow) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString className = windowClassName(window);
|
||||||
|
if (m_classBlacklist.contains(className)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Window> keepassxWindows = widgetsToX11Windows(QApplication::topLevelWidgets());
|
||||||
|
if (keepassxWindows.contains(window)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AutoTypePlatformX11::windowClassName(Window window)
|
||||||
|
{
|
||||||
|
QString className;
|
||||||
|
|
||||||
|
XClassHint wmClass;
|
||||||
|
wmClass.res_name = Q_NULLPTR;
|
||||||
|
wmClass.res_class = Q_NULLPTR;
|
||||||
|
|
||||||
|
if (XGetClassHint(m_dpy, window, &wmClass) && wmClass.res_name) {
|
||||||
|
className = QString::fromLocal8Bit(wmClass.res_name);
|
||||||
|
}
|
||||||
|
if (wmClass.res_name) {
|
||||||
|
XFree(wmClass.res_name);
|
||||||
|
}
|
||||||
|
if (wmClass.res_class) {
|
||||||
|
XFree(wmClass.res_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Window> AutoTypePlatformX11::widgetsToX11Windows(const QWidgetList& widgetList)
|
||||||
|
{
|
||||||
|
QList<Window> windows;
|
||||||
|
|
||||||
|
Q_FOREACH (const QWidget* widget, widgetList) {
|
||||||
|
windows.append(widget->winId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AutoTypePlatformX11::windowTitlesRecursive(Window window)
|
||||||
|
{
|
||||||
|
QStringList titles;
|
||||||
|
|
||||||
|
if (isTopLevelWindow(window)) {
|
||||||
|
QString title = windowTitle(window, true);
|
||||||
|
if (!title.isEmpty()) {
|
||||||
|
titles.append(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window root;
|
||||||
|
Window parent;
|
||||||
|
Window* children = Q_NULLPTR;
|
||||||
|
unsigned int numChildren;
|
||||||
|
if (XQueryTree(m_dpy, window, &root, &parent, &children, &numChildren) && children) {
|
||||||
|
for (uint i = 0; i < numChildren; i++) {
|
||||||
|
titles.append(windowTitlesRecursive(children[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (children) {
|
||||||
|
XFree(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return titles;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoTypePlatformX11::isTopLevelWindow(Window window)
|
||||||
|
{
|
||||||
|
Atom type = None;
|
||||||
|
int format;
|
||||||
|
unsigned long nitems;
|
||||||
|
unsigned long after;
|
||||||
|
unsigned char* data = Q_NULLPTR;
|
||||||
|
int retVal = XGetWindowProperty(m_dpy, window, m_atomWmState, 0, 0, false, AnyPropertyType, &type, &format,
|
||||||
|
&nitems, &after, &data);
|
||||||
|
if (data) {
|
||||||
|
XFree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (retVal == 0) && type;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else if (unicode >= 0x0100) {
|
||||||
|
return unicode | 0x01000000;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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_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;
|
||||||
|
default:
|
||||||
|
if (key >= Qt::Key_F1 && key <= Qt::Key_F16) {
|
||||||
|
return XK_F1 + (key - Qt::Key_F1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NoSymbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypePlatformX11::updateKeymap() {
|
||||||
|
ReadKeymap();
|
||||||
|
|
||||||
|
if (!m_altgrMask) {
|
||||||
|
AddModifier(XK_Mode_switch);
|
||||||
|
}
|
||||||
|
if (!m_metaMask) {
|
||||||
|
m_metaMask = Mod4Mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modifierMask = ControlMask | ShiftMask | m_altMask | m_metaMask;
|
||||||
|
|
||||||
|
if (m_currentGlobalKey && m_currentGlobalModifiers) {
|
||||||
|
unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||||
|
registerGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypePlatformX11::startCatchXErrors()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_catchXErrors);
|
||||||
|
|
||||||
|
m_catchXErrors = true;
|
||||||
|
m_xErrorOccured = 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_xErrorOccured = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
// The following code is taken from xvkbd 3.0 and has been slightly modified.
|
||||||
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a specified keysym to unused position in the keymap table.
|
||||||
|
* This will be called to add required keysyms on-the-fly.
|
||||||
|
* if the second parameter is TRUE, the keysym will be added to the
|
||||||
|
* non-shifted position - this may be required for modifier keys
|
||||||
|
* (e.g. Mode_switch) and some special keys (e.g. F20).
|
||||||
|
*/
|
||||||
|
int AutoTypePlatformX11::AddKeysym(KeySym keysym, bool top)
|
||||||
|
{
|
||||||
|
int keycode, pos, max_pos, inx, phase;
|
||||||
|
|
||||||
|
if (top) {
|
||||||
|
max_pos = 0;
|
||||||
|
} else {
|
||||||
|
max_pos = m_keysymPerKeycode - 1;
|
||||||
|
if (4 <= max_pos) max_pos = 3;
|
||||||
|
if (2 <= max_pos && m_altgrKeysym != XK_Mode_switch) max_pos = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (phase = 0; phase < 2; phase++) {
|
||||||
|
for (keycode = m_maxKeycode; m_minKeycode <= keycode; keycode--) {
|
||||||
|
for (pos = max_pos; 0 <= pos; pos--) {
|
||||||
|
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
|
||||||
|
if ((phase != 0 || m_keysymTable[inx] == NoSymbol) && m_keysymTable[inx] < 0xFF00) {
|
||||||
|
/* In the first phase, to avoid modifing existing keys, */
|
||||||
|
/* add the keysym only to the keys which has no keysym in the first position. */
|
||||||
|
/* If no place fuond in the first phase, add the keysym for any keys except */
|
||||||
|
/* for modifier keys and other special keys */
|
||||||
|
if (m_keysymTable[inx + pos] == NoSymbol) {
|
||||||
|
m_keysymTable[inx + pos] = keysym;
|
||||||
|
XChangeKeyboardMapping(m_dpy, keycode, m_keysymPerKeycode, &m_keysymTable[inx], 1);
|
||||||
|
XFlush(m_dpy);
|
||||||
|
return keycode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qWarning("Couldn't add \"%s\" to keymap", XKeysymToString(keysym));
|
||||||
|
return NoSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the specified key as a new modifier.
|
||||||
|
* This is used to use Mode_switch (AltGr) as a modifier.
|
||||||
|
*/
|
||||||
|
void AutoTypePlatformX11::AddModifier(KeySym keysym)
|
||||||
|
{
|
||||||
|
XModifierKeymap *modifiers;
|
||||||
|
int keycode, i, pos;
|
||||||
|
|
||||||
|
keycode = XKeysymToKeycode(m_dpy, keysym);
|
||||||
|
if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
|
||||||
|
|
||||||
|
modifiers = XGetModifierMapping(m_dpy);
|
||||||
|
for (i = 7; 3 < i; i--) {
|
||||||
|
if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol
|
||||||
|
|| ((m_keysymTable[(modifiers->modifiermap[i * modifiers->max_keypermod]
|
||||||
|
- m_minKeycode) * m_keysymPerKeycode]) == XK_ISO_Level3_Shift
|
||||||
|
&& keysym == XK_Mode_switch))
|
||||||
|
{
|
||||||
|
for (pos = 0; pos < modifiers->max_keypermod; pos++) {
|
||||||
|
if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) {
|
||||||
|
modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode;
|
||||||
|
XSetModifierMapping(m_dpy, modifiers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qWarning("Couldn't add \"%s\" as modifier", XKeysymToString(keysym));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read keyboard mapping and modifier mapping.
|
||||||
|
* Keyboard mapping is used to know what keys are in shifted position.
|
||||||
|
* Modifier mapping is required because we should know Alt and Meta
|
||||||
|
* key are used as which modifier.
|
||||||
|
*/
|
||||||
|
void AutoTypePlatformX11::ReadKeymap()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int keycode, inx, pos;
|
||||||
|
KeySym keysym;
|
||||||
|
XModifierKeymap *modifiers;
|
||||||
|
|
||||||
|
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
|
||||||
|
if (m_keysymTable != NULL) XFree(m_keysymTable);
|
||||||
|
m_keysymTable = XGetKeyboardMapping(m_dpy,
|
||||||
|
m_minKeycode, m_maxKeycode - m_minKeycode + 1,
|
||||||
|
&m_keysymPerKeycode);
|
||||||
|
for (keycode = m_minKeycode; keycode <= m_maxKeycode; keycode++) {
|
||||||
|
/* if the first keysym is alphabet and the second keysym is NoSymbol,
|
||||||
|
it is equivalent to pair of lowercase and uppercase alphabet */
|
||||||
|
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
|
||||||
|
if (m_keysymTable[inx + 1] == NoSymbol
|
||||||
|
&& ((XK_A <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_Z)
|
||||||
|
|| (XK_a <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_z)))
|
||||||
|
{
|
||||||
|
if (XK_A <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_Z)
|
||||||
|
m_keysymTable[inx] = m_keysymTable[inx] - XK_A + XK_a;
|
||||||
|
m_keysymTable[inx + 1] = m_keysymTable[inx] - XK_a + XK_A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_altMask = 0;
|
||||||
|
m_metaMask = 0;
|
||||||
|
m_altgrMask = 0;
|
||||||
|
m_altgrKeysym = NoSymbol;
|
||||||
|
modifiers = XGetModifierMapping(m_dpy);
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
for (pos = 0; pos < modifiers->max_keypermod; pos++) {
|
||||||
|
keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos];
|
||||||
|
if (keycode < m_minKeycode || m_maxKeycode < keycode) continue;
|
||||||
|
|
||||||
|
keysym = m_keysymTable[(keycode - m_minKeycode) * m_keysymPerKeycode];
|
||||||
|
if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
|
||||||
|
m_altMask = 1 << i;
|
||||||
|
} else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
|
||||||
|
m_metaMask = 1 << i;
|
||||||
|
} else if (keysym == XK_Mode_switch) {
|
||||||
|
if (m_altgrKeysym == XK_ISO_Level3_Shift) {
|
||||||
|
} else {
|
||||||
|
m_altgrMask = 0x0101 << i;
|
||||||
|
/* I don't know why, but 0x2000 was required for mod3 on my Linux box */
|
||||||
|
m_altgrKeysym = keysym;
|
||||||
|
}
|
||||||
|
} else if (keysym == XK_ISO_Level3_Shift) {
|
||||||
|
/* if no Mode_switch, try to use ISO_Level3_Shift instead */
|
||||||
|
/* however, it may not work as intended - I don't know why */
|
||||||
|
m_altgrMask = 1 << i;
|
||||||
|
m_altgrKeysym = keysym;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XFreeModifiermap(modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send event to the focused window.
|
||||||
|
* If input focus is specified explicitly, select the window
|
||||||
|
* before send event to the window.
|
||||||
|
*/
|
||||||
|
void AutoTypePlatformX11::SendEvent(XKeyEvent *event)
|
||||||
|
{
|
||||||
|
XSync(event->display, FALSE);
|
||||||
|
int (*oldHandler) (Display*, XErrorEvent*) = XSetErrorHandler(MyErrorHandler);
|
||||||
|
|
||||||
|
XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0);
|
||||||
|
XFlush(event->display);
|
||||||
|
|
||||||
|
XSetErrorHandler(oldHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send sequence of KeyPressed/KeyReleased events to the focused
|
||||||
|
* window to simulate keyboard. If modifiers (shift, control, etc)
|
||||||
|
* are set ON, many events will be sent.
|
||||||
|
*/
|
||||||
|
void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym, unsigned int shift)
|
||||||
|
{
|
||||||
|
Window cur_focus;
|
||||||
|
int revert_to;
|
||||||
|
XKeyEvent event;
|
||||||
|
int keycode;
|
||||||
|
int phase, inx;
|
||||||
|
bool found;
|
||||||
|
|
||||||
|
XGetInputFocus(m_dpy, &cur_focus, &revert_to);
|
||||||
|
|
||||||
|
found = FALSE;
|
||||||
|
keycode = 0;
|
||||||
|
if (keysym != NoSymbol) {
|
||||||
|
for (phase = 0; phase < 2; phase++) {
|
||||||
|
for (keycode = m_minKeycode; !found && (keycode <= m_maxKeycode); keycode++) {
|
||||||
|
/* Determine keycode for the keysym: we use this instead
|
||||||
|
of XKeysymToKeycode() because we must know shift_state, too */
|
||||||
|
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
|
||||||
|
if (m_keysymTable[inx] == keysym) {
|
||||||
|
shift &= ~m_altgrMask;
|
||||||
|
if (m_keysymTable[inx + 1] != NoSymbol) shift &= ~ShiftMask;
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
} else if (m_keysymTable[inx + 1] == keysym) {
|
||||||
|
shift &= ~m_altgrMask;
|
||||||
|
shift |= ShiftMask;
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found && m_altgrMask && 3 <= m_keysymPerKeycode) {
|
||||||
|
for (keycode = m_minKeycode; !found && (keycode <= m_maxKeycode); keycode++) {
|
||||||
|
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
|
||||||
|
if (m_keysymTable[inx + 2] == keysym) {
|
||||||
|
shift &= ~ShiftMask;
|
||||||
|
shift |= m_altgrMask;
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
} else if (4 <= m_keysymPerKeycode && m_keysymTable[inx + 3] == keysym) {
|
||||||
|
shift |= ShiftMask | m_altgrMask;
|
||||||
|
found = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (found) break;
|
||||||
|
|
||||||
|
if (0xF000 <= keysym) {
|
||||||
|
/* for special keys such as function keys,
|
||||||
|
first try to add it in the non-shifted position of the keymap */
|
||||||
|
if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE);
|
||||||
|
} else {
|
||||||
|
AddKeysym(keysym, FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.display = m_dpy;
|
||||||
|
event.window = cur_focus;
|
||||||
|
event.root = m_rootWindow;
|
||||||
|
event.subwindow = None;
|
||||||
|
event.time = CurrentTime;
|
||||||
|
event.x = 1;
|
||||||
|
event.y = 1;
|
||||||
|
event.x_root = 1;
|
||||||
|
event.y_root = 1;
|
||||||
|
event.same_screen = TRUE;
|
||||||
|
|
||||||
|
Window root, child;
|
||||||
|
int root_x, root_y, x, y;
|
||||||
|
unsigned int mask;
|
||||||
|
|
||||||
|
XQueryPointer(m_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
|
||||||
|
|
||||||
|
event.type = KeyRelease;
|
||||||
|
event.state = 0;
|
||||||
|
if (mask & ControlMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
if (mask & m_altMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
if (mask & m_metaMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
if (mask & m_altgrMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
if (mask & ShiftMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
if (mask & LockMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Caps_Lock);
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.type = KeyPress;
|
||||||
|
event.state = 0;
|
||||||
|
if (shift & ControlMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state |= ControlMask;
|
||||||
|
}
|
||||||
|
if (shift & m_altMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state |= m_altMask;
|
||||||
|
}
|
||||||
|
if (shift & m_metaMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state |= m_metaMask;
|
||||||
|
}
|
||||||
|
if (shift & m_altgrMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state |= m_altgrMask;
|
||||||
|
}
|
||||||
|
if (shift & ShiftMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state |= ShiftMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym != NoSymbol) { /* send event for the key itself */
|
||||||
|
event.keycode = found ? keycode : XKeysymToKeycode(m_dpy, keysym);
|
||||||
|
if (event.keycode == NoSymbol) {
|
||||||
|
if ((keysym & ~0x7f) == 0 && QChar(static_cast<char>(keysym)).isPrint())
|
||||||
|
qWarning("No such key: %c", static_cast<char>(keysym));
|
||||||
|
else if (XKeysymToString(keysym) != NULL)
|
||||||
|
qWarning("No such key: keysym=%s (0x%lX)", XKeysymToString(keysym), static_cast<long>(keysym));
|
||||||
|
else
|
||||||
|
qWarning("No such key: keysym=0x%lX", static_cast<long>(keysym));
|
||||||
|
} else {
|
||||||
|
SendEvent(&event);
|
||||||
|
event.type = KeyRelease;
|
||||||
|
SendEvent(&event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.type = KeyRelease;
|
||||||
|
if (shift & ShiftMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state &= ~ShiftMask;
|
||||||
|
}
|
||||||
|
if (shift & m_altgrMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state &= ~m_altgrMask;
|
||||||
|
}
|
||||||
|
if (shift & m_metaMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state &= ~m_metaMask;
|
||||||
|
}
|
||||||
|
if (shift & m_altMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state &= ~m_altMask;
|
||||||
|
}
|
||||||
|
if (shift & ControlMask) {
|
||||||
|
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L);
|
||||||
|
SendEvent(&event);
|
||||||
|
event.state &= ~ControlMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AutoTypePlatformX11::MyErrorHandler(Display *my_dpy, XErrorEvent *event)
|
||||||
|
{
|
||||||
|
char msg[200];
|
||||||
|
|
||||||
|
if (event->error_code == BadWindow) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
XGetErrorText(my_dpy, event->error_code, msg, sizeof(msg) - 1);
|
||||||
|
qWarning("X error trapped: %s, request-code=%d\n", msg, event->request_code);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AutoTypeExecturorX11::AutoTypeExecturorX11(AutoTypePlatformX11* platform)
|
||||||
|
: m_platform(platform)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecturorX11::execChar(AutoTypeChar* action)
|
||||||
|
{
|
||||||
|
m_platform->SendKeyPressedEvent(m_platform->charToKeySym(action->character));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoTypeExecturorX11::execKey(AutoTypeKey* action)
|
||||||
|
{
|
||||||
|
m_platform->SendKeyPressedEvent(m_platform->keyToKeySym(action->key));
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_EXPORT_PLUGIN2(keepassx-autotype-x11, AutoTypePlatformX11)
|
113
src/autotype/x11/AutoTypeX11.h
Normal file
113
src/autotype/x11/AutoTypeX11.h
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
|
* Copyright (C) 2000-2008 Tom Sato <VEF00200@nifty.ne.jp>
|
||||||
|
*
|
||||||
|
* 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 KEEPASSX_AUTOTYPEX11_H
|
||||||
|
#define KEEPASSX_AUTOTYPEX11_H
|
||||||
|
|
||||||
|
#include <QtCore/QSet>
|
||||||
|
#include <QtCore/QtPlugin>
|
||||||
|
#include <QtGui/QApplication>
|
||||||
|
#include <QtGui/QWidget>
|
||||||
|
#include <QtGui/QX11Info>
|
||||||
|
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/extensions/XTest.h>
|
||||||
|
|
||||||
|
#include "autotype/AutoTypePlatformPlugin.h"
|
||||||
|
#include "autotype/AutoTypeAction.h"
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
|
class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(AutoTypePlatformInterface)
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoTypePlatformX11();
|
||||||
|
QStringList windowTitles();
|
||||||
|
WId activeWindow();
|
||||||
|
QString activeWindowTitle();
|
||||||
|
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
|
void unregisterGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
|
||||||
|
int platformEventFilter(void* event);
|
||||||
|
AutoTypeExecutor* createExecutor();
|
||||||
|
|
||||||
|
KeySym charToKeySym(const QChar& ch);
|
||||||
|
KeySym keyToKeySym(Qt::Key key);
|
||||||
|
|
||||||
|
void SendKeyPressedEvent(KeySym keysym, unsigned int shift = 0);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void globalShortcutTriggered();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString windowTitle(Window window, bool useBlacklist);
|
||||||
|
QStringList windowTitlesRecursive(Window window);
|
||||||
|
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);
|
||||||
|
|
||||||
|
void updateKeymap();
|
||||||
|
int AddKeysym(KeySym keysym, bool top);
|
||||||
|
void AddModifier(KeySym keysym);
|
||||||
|
void ReadKeymap();
|
||||||
|
void SendEvent(XKeyEvent *event);
|
||||||
|
static int MyErrorHandler(Display *my_dpy, XErrorEvent *event);
|
||||||
|
|
||||||
|
Display* m_dpy;
|
||||||
|
Window m_rootWindow;
|
||||||
|
Atom m_atomWmState;
|
||||||
|
Atom m_atomWmName;
|
||||||
|
Atom m_atomUtf8String;
|
||||||
|
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_xErrorOccured;
|
||||||
|
static int (*m_oldXErrorHandler) (Display*, XErrorEvent*);
|
||||||
|
|
||||||
|
KeySym* m_keysymTable;
|
||||||
|
int m_minKeycode;
|
||||||
|
int m_maxKeycode;
|
||||||
|
int m_keysymPerKeycode;
|
||||||
|
int m_altMask;
|
||||||
|
int m_metaMask;
|
||||||
|
int m_altgrMask;
|
||||||
|
KeySym m_altgrKeysym;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AutoTypeExecturorX11 : public AutoTypeExecutor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit AutoTypeExecturorX11(AutoTypePlatformX11* platform);
|
||||||
|
|
||||||
|
void execChar(AutoTypeChar* action);
|
||||||
|
void execKey(AutoTypeKey* action);
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoTypePlatformX11* const m_platform;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_AUTOTYPEX11_H
|
14
src/autotype/x11/CMakeLists.txt
Normal file
14
src/autotype/x11/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
include_directories(SYSTEM ${X11_X11_INCLUDE_PATH})
|
||||||
|
|
||||||
|
set(autotype_X11_SOURCES
|
||||||
|
AutoTypeX11.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(autotype_X11_MOC
|
||||||
|
AutoTypeX11.h
|
||||||
|
)
|
||||||
|
|
||||||
|
qt4_wrap_cpp(autotype_X11_SOURCES ${autotype_X11_MOC})
|
||||||
|
|
||||||
|
add_library(keepassx-autotype-x11 MODULE ${autotype_X11_SOURCES})
|
||||||
|
target_link_libraries(keepassx-autotype-x11 ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${X11_X11_LIB} ${X11_XTest_LIB})
|
@ -171,6 +171,60 @@ const QList<AutoTypeAssociation>& Entry::autoTypeAssociations() const
|
|||||||
return m_data.autoTypeAssociations;
|
return m_data.autoTypeAssociations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::autoTypeSequence(const QString& windowTitle) const
|
||||||
|
{
|
||||||
|
if (!m_data.autoTypeEnabled) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enableSet = false;
|
||||||
|
QString sequence;
|
||||||
|
if (!windowTitle.isEmpty()) {
|
||||||
|
Q_FOREACH (const AutoTypeAssociation& autoTypeAssoc, m_data.autoTypeAssociations) {
|
||||||
|
if (windowMatches(windowTitle, autoTypeAssoc.window)) {
|
||||||
|
sequence = autoTypeAssoc.sequence;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequence.isEmpty()) {
|
||||||
|
sequence = m_data.defaultAutoTypeSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
Group* group = m_group;
|
||||||
|
do {
|
||||||
|
if (!enableSet) {
|
||||||
|
if (group->autoTypeEnabled() == Group::Disable) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
else if (group->autoTypeEnabled() == Group::Enable) {
|
||||||
|
enableSet = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sequence.isEmpty()) {
|
||||||
|
sequence = group->defaultAutoTypeSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
group = group->parentGroup();
|
||||||
|
} while (group && (!enableSet || sequence.isEmpty()));
|
||||||
|
|
||||||
|
if (sequence.isEmpty() && (!username().isEmpty() || !password().isEmpty())) {
|
||||||
|
if (username().isEmpty()) {
|
||||||
|
sequence = "{PASSWORD}{ENTER}";
|
||||||
|
}
|
||||||
|
else if (password().isEmpty()) {
|
||||||
|
sequence = "{USERNAME}{ENTER}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
QString Entry::title() const
|
QString Entry::title() const
|
||||||
{
|
{
|
||||||
return m_attributes->value("Title");
|
return m_attributes->value("Title");
|
||||||
@ -536,3 +590,35 @@ bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity
|
|||||||
url().contains(searchTerm, caseSensitivity) ||
|
url().contains(searchTerm, caseSensitivity) ||
|
||||||
notes().contains(searchTerm, caseSensitivity);
|
notes().contains(searchTerm, caseSensitivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entry::windowMatches(const QString& windowTitle, const QString& windowPattern)
|
||||||
|
{
|
||||||
|
QRegExp regExp;
|
||||||
|
regExp.setCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
|
||||||
|
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
|
||||||
|
regExp.setPatternSyntax(QRegExp::RegExp2);
|
||||||
|
regExp.setPattern(windowPattern.mid(2, windowPattern.size() - 4));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
regExp.setPatternSyntax(QRegExp::Wildcard);
|
||||||
|
regExp.setPattern(windowPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
return regExp.exactMatch(windowTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Entry::resolvePlaceholders(const QString& str) const
|
||||||
|
{
|
||||||
|
QString result = str;
|
||||||
|
|
||||||
|
result.replace("{TITLE}", title(), Qt::CaseInsensitive);
|
||||||
|
result.replace("{USERNAME}", username(), Qt::CaseInsensitive);
|
||||||
|
result.replace("{URL}", url(), Qt::CaseInsensitive);
|
||||||
|
result.replace("{PASSWORD}", password(), Qt::CaseInsensitive);
|
||||||
|
result.replace("{NOTES}", notes(), Qt::CaseInsensitive);
|
||||||
|
|
||||||
|
// TODO: lots of other placeholders missing
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -80,6 +80,7 @@ public:
|
|||||||
int autoTypeObfuscation() const;
|
int autoTypeObfuscation() const;
|
||||||
QString defaultAutoTypeSequence() const;
|
QString defaultAutoTypeSequence() const;
|
||||||
const QList<AutoTypeAssociation>& autoTypeAssociations() const;
|
const QList<AutoTypeAssociation>& autoTypeAssociations() const;
|
||||||
|
QString autoTypeSequence(const QString& windowTitle = QString()) const;
|
||||||
QString title() const;
|
QString title() const;
|
||||||
QString url() const;
|
QString url() const;
|
||||||
QString username() const;
|
QString username() const;
|
||||||
@ -120,6 +121,7 @@ public:
|
|||||||
void removeHistoryItems(QList<Entry*> historyEntries);
|
void removeHistoryItems(QList<Entry*> historyEntries);
|
||||||
void truncateHistory();
|
void truncateHistory();
|
||||||
Entry* clone() const;
|
Entry* clone() const;
|
||||||
|
QString resolvePlaceholders(const QString& str) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call before and after set*() methods to create a history item
|
* Call before and after set*() methods to create a history item
|
||||||
@ -150,6 +152,7 @@ private Q_SLOTS:
|
|||||||
private:
|
private:
|
||||||
const Database* database() const;
|
const Database* database() const;
|
||||||
template <class T> inline bool set(T& property, const T& value);
|
template <class T> inline bool set(T& property, const T& value);
|
||||||
|
static bool windowMatches(const QString& windowTitle, const QString& windowPattern);
|
||||||
|
|
||||||
Uuid m_uuid;
|
Uuid m_uuid;
|
||||||
EntryData m_data;
|
EntryData m_data;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Tobias Tangemann
|
* Copyright (C) 2012 Tobias Tangemann
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -19,6 +20,8 @@
|
|||||||
|
|
||||||
#include <QtGui/QFileOpenEvent>
|
#include <QtGui/QFileOpenEvent>
|
||||||
|
|
||||||
|
#include "autotype/AutoType.h"
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv)
|
Application::Application(int& argc, char** argv)
|
||||||
: QApplication(argc, argv)
|
: QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
@ -34,3 +37,19 @@ bool Application::event(QEvent* event)
|
|||||||
|
|
||||||
return QApplication::event(event);
|
return QApplication::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
bool Application::x11EventFilter(XEvent* event)
|
||||||
|
{
|
||||||
|
int retCode = autoType()->callEventFilter(event);
|
||||||
|
|
||||||
|
if (retCode == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (retCode == 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QApplication::x11EventFilter(event);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012 Tobias Tangemann
|
* Copyright (C) 2012 Tobias Tangemann
|
||||||
|
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -30,6 +31,9 @@ public:
|
|||||||
Application(int& argc, char** argv);
|
Application(int& argc, char** argv);
|
||||||
|
|
||||||
bool event(QEvent* event) Q_DECL_OVERRIDE;
|
bool event(QEvent* event) Q_DECL_OVERRIDE;
|
||||||
|
#ifdef Q_WS_X11
|
||||||
|
bool x11EventFilter(XEvent* event) Q_DECL_OVERRIDE;
|
||||||
|
#endif
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void openFile(const QString& filename);
|
void openFile(const QString& filename);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user