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:
Felix Geyer 2012-07-12 22:33:20 +02:00
parent 073f3f9dfc
commit bc207714da
18 changed files with 1905 additions and 0 deletions

View File

@ -89,6 +89,8 @@ endif()
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,-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()
if(WITH_LTO)

View File

@ -24,6 +24,7 @@ Copyright: 2010-2012, Felix Geyer <debfx@fobos.de>
2007, Trolltech ASA <info@trolltech.com>
2012, Intel Corporation
2012, Nokia Corporation and/or its subsidiary(-ies)
2000-2008, Tom Sato <VEF00200@nifty.ne.jp>
License: GPL-2 or GPL-3
Files: share/icons/database/*.png

View File

@ -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)
set(keepassx_SOURCES
autotype/AutoType.cpp
autotype/AutoTypeAction.cpp
autotype/ShortcutWidget.cpp
core/Config.cpp
core/Database.cpp
core/DatabaseIcons.cpp
@ -92,6 +95,8 @@ set(keepassx_SOURCES
)
set(keepassx_MOC
autotype/AutoType.h
autotype/ShortcutWidget.h
core/Config.h
core/Database.h
core/Entry.h
@ -181,6 +186,8 @@ install(TARGETS ${PROGNAME}
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION ${BIN_INSTALL_DIR} COMPONENT Runtime)
add_subdirectory(autotype)
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")
install(DIRECTORY "${QT_LIBRARY_DIR}/Resources/qt_menu.nib"

406
src/autotype/AutoType.cpp Normal file
View 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
View 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

View 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
}

View 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

View 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

View File

@ -0,0 +1,7 @@
if(Q_WS_X11)
find_package(X11)
if(X11_FOUND AND X11_XTest_FOUND)
add_subdirectory(x11)
endif()
endif()

View 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));
}

View 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

View 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)

View 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

View 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})

View File

@ -171,6 +171,60 @@ const QList<AutoTypeAssociation>& Entry::autoTypeAssociations() const
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
{
return m_attributes->value("Title");
@ -536,3 +590,35 @@ bool Entry::match(const QString& searchTerm, Qt::CaseSensitivity caseSensitivity
url().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;
}

View File

@ -80,6 +80,7 @@ public:
int autoTypeObfuscation() const;
QString defaultAutoTypeSequence() const;
const QList<AutoTypeAssociation>& autoTypeAssociations() const;
QString autoTypeSequence(const QString& windowTitle = QString()) const;
QString title() const;
QString url() const;
QString username() const;
@ -120,6 +121,7 @@ public:
void removeHistoryItems(QList<Entry*> historyEntries);
void truncateHistory();
Entry* clone() const;
QString resolvePlaceholders(const QString& str) const;
/**
* Call before and after set*() methods to create a history item
@ -150,6 +152,7 @@ private Q_SLOTS:
private:
const Database* database() const;
template <class T> inline bool set(T& property, const T& value);
static bool windowMatches(const QString& windowTitle, const QString& windowPattern);
Uuid m_uuid;
EntryData m_data;

View File

@ -1,5 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@ -19,6 +20,8 @@
#include <QtGui/QFileOpenEvent>
#include "autotype/AutoType.h"
Application::Application(int& argc, char** argv)
: QApplication(argc, argv)
{
@ -34,3 +37,19 @@ bool Application::event(QEvent* 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

View File

@ -1,5 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@ -30,6 +31,9 @@ public:
Application(int& argc, char** argv);
bool event(QEvent* event) Q_DECL_OVERRIDE;
#ifdef Q_WS_X11
bool x11EventFilter(XEvent* event) Q_DECL_OVERRIDE;
#endif
Q_SIGNALS:
void openFile(const QString& filename);