mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-23 13:11:12 -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")
|
||||
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)
|
||||
|
1
COPYING
1
COPYING
@ -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
|
||||
|
@ -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
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user