Auto-Type: Allow actions to fail and be retried

AutoTypeActions are required to return a result object with
information if they can be retried or not. An error string is
also provided to show a user friendly message why said action did
not succeed if even after retries it keeps failing.

This is a prerequisite for waiting for modifier keys to be released
on X11.
This commit is contained in:
Toni Spets 2021-03-27 13:44:08 +02:00 committed by Jonathan White
parent 4d07507739
commit 8c9530e3ec
11 changed files with 143 additions and 51 deletions

View File

@ -294,7 +294,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
window = m_plugin->activeWindow(); window = m_plugin->activeWindow();
} }
Tools::wait(qMax(100, config()->get(Config::AutoTypeStartDelay).toInt())); int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
Tools::wait(delay);
for (const auto& action : asConst(actions)) { for (const auto& action : asConst(actions)) {
if (m_plugin->activeWindow() != window) { if (m_plugin->activeWindow() != window) {
@ -304,8 +305,25 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
return; return;
} }
action->exec(m_executor); constexpr int max_retries = 5;
for (int i = 1; i <= max_retries; i++) {
auto result = action->exec(m_executor);
QCoreApplication::processEvents(QEventLoop::AllEvents, 10); QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
if (result.isOk()) {
break;
}
if (!result.canRetry() || i == max_retries) {
MessageBox::critical(getMainWindow(), tr("Auto-Type Error"), result.errorString());
emit autotypeRejected();
m_inAutoType.unlock();
return;
}
Tools::wait(delay);
}
} }
m_windowForGlobal = 0; m_windowForGlobal = 0;

View File

@ -32,9 +32,9 @@ AutoTypeKey::AutoTypeKey(const QChar& character, Qt::KeyboardModifiers modifiers
{ {
} }
void AutoTypeKey::exec(AutoTypeExecutor* executor) const AutoTypeAction::Result AutoTypeKey::exec(AutoTypeExecutor* executor) const
{ {
executor->execType(this); return executor->execType(this);
} }
AutoTypeDelay::AutoTypeDelay(int delayMs, bool setExecDelay) AutoTypeDelay::AutoTypeDelay(int delayMs, bool setExecDelay)
@ -43,7 +43,7 @@ AutoTypeDelay::AutoTypeDelay(int delayMs, bool setExecDelay)
{ {
} }
void AutoTypeDelay::exec(AutoTypeExecutor* executor) const AutoTypeAction::Result AutoTypeDelay::exec(AutoTypeExecutor* executor) const
{ {
if (setExecDelay) { if (setExecDelay) {
// Change the delay between actions // Change the delay between actions
@ -52,14 +52,16 @@ void AutoTypeDelay::exec(AutoTypeExecutor* executor) const
// Pause execution // Pause execution
Tools::wait(delayMs); Tools::wait(delayMs);
} }
return AutoTypeAction::Result::Ok();
} }
void AutoTypeClearField::exec(AutoTypeExecutor* executor) const AutoTypeAction::Result AutoTypeClearField::exec(AutoTypeExecutor* executor) const
{ {
executor->execClearField(this); return executor->execClearField(this);
} }
void AutoTypeBegin::exec(AutoTypeExecutor* executor) const AutoTypeAction::Result AutoTypeBegin::exec(AutoTypeExecutor* executor) const
{ {
executor->execBegin(this); return executor->execBegin(this);
} }

View File

@ -28,8 +28,61 @@ class AutoTypeExecutor;
class KEEPASSXC_EXPORT AutoTypeAction class KEEPASSXC_EXPORT AutoTypeAction
{ {
public: public:
class Result
{
public:
Result()
: m_isOk(false)
, m_canRetry(false)
, m_error(QString())
{
}
static Result Ok()
{
return Result(true, false, QString());
}
static Result Retry(const QString& error)
{
return Result(false, true, error);
}
static Result Failed(const QString& error)
{
return Result(false, false, error);
}
bool isOk() const
{
return m_isOk;
}
bool canRetry() const
{
return m_canRetry;
}
const QString& errorString() const
{
return m_error;
}
private:
bool m_isOk;
bool m_canRetry;
QString m_error;
Result(bool isOk, bool canRetry, const QString& error)
: m_isOk(isOk)
, m_canRetry(canRetry)
, m_error(error)
{
}
};
AutoTypeAction() = default; AutoTypeAction() = default;
virtual void exec(AutoTypeExecutor* executor) const = 0; virtual Result exec(AutoTypeExecutor* executor) const = 0;
virtual ~AutoTypeAction() = default; virtual ~AutoTypeAction() = default;
}; };
@ -38,7 +91,7 @@ class KEEPASSXC_EXPORT AutoTypeKey : public AutoTypeAction
public: public:
explicit AutoTypeKey(const QChar& character, Qt::KeyboardModifiers modifiers = Qt::NoModifier); explicit AutoTypeKey(const QChar& character, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
explicit AutoTypeKey(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier); explicit AutoTypeKey(Qt::Key key, Qt::KeyboardModifiers modifiers = Qt::NoModifier);
void exec(AutoTypeExecutor* executor) const override; Result exec(AutoTypeExecutor* executor) const override;
const QChar character; const QChar character;
const Qt::Key key = Qt::Key_unknown; const Qt::Key key = Qt::Key_unknown;
@ -49,7 +102,7 @@ class KEEPASSXC_EXPORT AutoTypeDelay : public AutoTypeAction
{ {
public: public:
explicit AutoTypeDelay(int delayMs, bool setExecDelay = false); explicit AutoTypeDelay(int delayMs, bool setExecDelay = false);
void exec(AutoTypeExecutor* executor) const override; Result exec(AutoTypeExecutor* executor) const override;
const int delayMs; const int delayMs;
const bool setExecDelay; const bool setExecDelay;
@ -58,24 +111,25 @@ public:
class KEEPASSXC_EXPORT AutoTypeClearField : public AutoTypeAction class KEEPASSXC_EXPORT AutoTypeClearField : public AutoTypeAction
{ {
public: public:
void exec(AutoTypeExecutor* executor) const override; Result exec(AutoTypeExecutor* executor) const override;
}; };
class KEEPASSXC_EXPORT AutoTypeBegin : public AutoTypeAction class KEEPASSXC_EXPORT AutoTypeBegin : public AutoTypeAction
{ {
public: public:
void exec(AutoTypeExecutor* executor) const override; Result exec(AutoTypeExecutor* executor) const override;
}; };
class KEEPASSXC_EXPORT AutoTypeExecutor class KEEPASSXC_EXPORT AutoTypeExecutor
{ {
public: public:
virtual ~AutoTypeExecutor() = default; virtual ~AutoTypeExecutor() = default;
virtual void execBegin(const AutoTypeBegin* action) = 0; virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
virtual void execType(const AutoTypeKey* action) = 0; virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
virtual void execClearField(const AutoTypeClearField* action) = 0; virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;
int execDelayMs = 25; int execDelayMs = 25;
QString error;
}; };
#endif // KEEPASSX_AUTOTYPEACTION_H #endif // KEEPASSX_AUTOTYPEACTION_H

View File

@ -215,12 +215,13 @@ AutoTypeExecutorMac::AutoTypeExecutorMac(AutoTypePlatformMac* platform)
{ {
} }
void AutoTypeExecutorMac::execBegin(const AutoTypeBegin* action) AutoTypeAction::Result AutoTypeExecutorMac::execBegin(const AutoTypeBegin* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorMac::execType(const AutoTypeKey* action) AutoTypeAction::Result AutoTypeExecutorMac::execType(const AutoTypeKey* action)
{ {
if (action->modifiers & Qt::ShiftModifier) { if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, true); m_platform->sendKey(Qt::Key_Shift, true);
@ -251,12 +252,14 @@ void AutoTypeExecutorMac::execType(const AutoTypeKey* action)
} }
Tools::sleep(execDelayMs); Tools::sleep(execDelayMs);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorMac::execClearField(const AutoTypeClearField* action) AutoTypeAction::Result AutoTypeExecutorMac::execClearField(const AutoTypeClearField* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
execType(new AutoTypeKey(Qt::Key_Up, Qt::ControlModifier)); execType(new AutoTypeKey(Qt::Key_Up, Qt::ControlModifier));
execType(new AutoTypeKey(Qt::Key_Down, Qt::ControlModifier | Qt::ShiftModifier)); execType(new AutoTypeKey(Qt::Key_Down, Qt::ControlModifier | Qt::ShiftModifier));
execType(new AutoTypeKey(Qt::Key_Backspace)); execType(new AutoTypeKey(Qt::Key_Backspace));
return AutoTypeAction::Result::Ok();
} }

View File

@ -57,9 +57,9 @@ class AutoTypeExecutorMac : public AutoTypeExecutor
public: public:
explicit AutoTypeExecutorMac(AutoTypePlatformMac* platform); explicit AutoTypeExecutorMac(AutoTypePlatformMac* platform);
void execBegin(const AutoTypeBegin* action) override; AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
void execType(const AutoTypeKey* action) override; AutoTypeAction::Result execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override; AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
private: private:
AutoTypePlatformMac* const m_platform; AutoTypePlatformMac* const m_platform;

View File

@ -102,17 +102,20 @@ AutoTypeExecutorTest::AutoTypeExecutorTest(AutoTypePlatformTest* platform)
{ {
} }
void AutoTypeExecutorTest::execBegin(const AutoTypeBegin* action) AutoTypeAction::Result AutoTypeExecutorTest::execBegin(const AutoTypeBegin* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorTest::execType(const AutoTypeKey* action) AutoTypeAction::Result AutoTypeExecutorTest::execType(const AutoTypeKey* action)
{ {
m_platform->addAction(action); m_platform->addAction(action);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorTest::execClearField(const AutoTypeClearField* action) AutoTypeAction::Result AutoTypeExecutorTest::execClearField(const AutoTypeClearField* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
return AutoTypeAction::Result::Ok();
} }

View File

@ -64,9 +64,9 @@ class AutoTypeExecutorTest : public AutoTypeExecutor
public: public:
explicit AutoTypeExecutorTest(AutoTypePlatformTest* platform); explicit AutoTypeExecutorTest(AutoTypePlatformTest* platform);
void execBegin(const AutoTypeBegin* action) override; AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
void execType(const AutoTypeKey* action) override; AutoTypeAction::Result execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override; AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
private: private:
AutoTypePlatformTest* const m_platform; AutoTypePlatformTest* const m_platform;

View File

@ -226,12 +226,13 @@ AutoTypeExecutorWin::AutoTypeExecutorWin(AutoTypePlatformWin* platform)
{ {
} }
void AutoTypeExecutorWin::execBegin(const AutoTypeBegin* action) AutoTypeAction::Result AutoTypeExecutorWin::execBegin(const AutoTypeBegin* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorWin::execType(const AutoTypeKey* action) AutoTypeAction::Result AutoTypeExecutorWin::execType(const AutoTypeKey* action)
{ {
if (action->modifiers & Qt::ShiftModifier) { if (action->modifiers & Qt::ShiftModifier) {
m_platform->sendKey(Qt::Key_Shift, true); m_platform->sendKey(Qt::Key_Shift, true);
@ -262,12 +263,14 @@ void AutoTypeExecutorWin::execType(const AutoTypeKey* action)
} }
Tools::sleep(execDelayMs); Tools::sleep(execDelayMs);
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorWin::execClearField(const AutoTypeClearField* action) AutoTypeAction::Result AutoTypeExecutorWin::execClearField(const AutoTypeClearField* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier)); execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier));
execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier)); execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier));
execType(new AutoTypeKey(Qt::Key_Backspace)); execType(new AutoTypeKey(Qt::Key_Backspace));
return AutoTypeAction::Result::Ok();
} }

View File

@ -54,9 +54,9 @@ class AutoTypeExecutorWin : public AutoTypeExecutor
public: public:
explicit AutoTypeExecutorWin(AutoTypePlatformWin* platform); explicit AutoTypeExecutorWin(AutoTypePlatformWin* platform);
void execBegin(const AutoTypeBegin* action) override; AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
void execType(const AutoTypeKey* action) override; AutoTypeAction::Result execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override; AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
private: private:
AutoTypePlatformWin* const m_platform; AutoTypePlatformWin* const m_platform;

View File

@ -395,11 +395,10 @@ bool AutoTypePlatformX11::GetKeycode(KeySym keysym, int* keycode, int* group, un
* window to simulate keyboard. If modifiers (shift, control, etc) * window to simulate keyboard. If modifiers (shift, control, etc)
* are set ON, many events will be sent. * are set ON, many events will be sent.
*/ */
void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers) AutoTypeAction::Result AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
{ {
if (keysym == NoSymbol) { if (keysym == NoSymbol) {
qWarning("No such key: keysym=0x%lX", keysym); return AutoTypeAction::Result::Failed(tr("Trying to send invalid keysym."));
return;
} }
int keycode; int keycode;
@ -417,8 +416,8 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
/* determine keycode, group and mask for the given keysym */ /* determine keycode, group and mask for the given keysym */
if (!GetKeycode(keysym, &keycode, &group, &wanted_mask)) { if (!GetKeycode(keysym, &keycode, &group, &wanted_mask)) {
qWarning("Unable to get valid keycode for key: keysym=0x%lX", keysym); return AutoTypeAction::Result::Failed(tr("Unable to get valid keycode for key: ")
return; + QString(XKeysymToString(keysym)));
} }
wanted_mask |= modifiers; wanted_mask |= modifiers;
@ -498,6 +497,8 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
XkbLockGroup(m_dpy, XkbUseCoreKbd, group_active); XkbLockGroup(m_dpy, XkbUseCoreKbd, group_active);
XFlush(m_dpy); XFlush(m_dpy);
} }
return AutoTypeAction::Result::Ok();
} }
int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event) int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event)
@ -517,29 +518,37 @@ AutoTypeExecutorX11::AutoTypeExecutorX11(AutoTypePlatformX11* platform)
{ {
} }
void AutoTypeExecutorX11::execBegin(const AutoTypeBegin* action) AutoTypeAction::Result AutoTypeExecutorX11::execBegin(const AutoTypeBegin* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
m_platform->updateKeymap(); m_platform->updateKeymap();
return AutoTypeAction::Result::Ok();
} }
void AutoTypeExecutorX11::execType(const AutoTypeKey* action) AutoTypeAction::Result AutoTypeExecutorX11::execType(const AutoTypeKey* action)
{ {
AutoTypeAction::Result result;
if (action->key != Qt::Key_unknown) { if (action->key != Qt::Key_unknown) {
m_platform->sendKey(qtToNativeKeyCode(action->key), qtToNativeModifiers(action->modifiers)); result = m_platform->sendKey(qtToNativeKeyCode(action->key), qtToNativeModifiers(action->modifiers));
} else { } else {
m_platform->sendKey(qcharToNativeKeyCode(action->character), qtToNativeModifiers(action->modifiers)); result = m_platform->sendKey(qcharToNativeKeyCode(action->character), qtToNativeModifiers(action->modifiers));
} }
if (result.isOk()) {
Tools::sleep(execDelayMs); Tools::sleep(execDelayMs);
} }
void AutoTypeExecutorX11::execClearField(const AutoTypeClearField* action) return result;
}
AutoTypeAction::Result AutoTypeExecutorX11::execClearField(const AutoTypeClearField* action)
{ {
Q_UNUSED(action); Q_UNUSED(action);
execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier)); execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier));
execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier)); execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier));
execType(new AutoTypeKey(Qt::Key_Backspace)); execType(new AutoTypeKey(Qt::Key_Backspace));
return AutoTypeAction::Result::Ok();
} }
bool AutoTypePlatformX11::raiseWindow(WId window) bool AutoTypePlatformX11::raiseWindow(WId window)

View File

@ -56,7 +56,7 @@ public:
AutoTypeExecutor* createExecutor() override; AutoTypeExecutor* createExecutor() override;
void updateKeymap(); void updateKeymap();
void sendKey(KeySym keysym, unsigned int modifiers = 0); AutoTypeAction::Result sendKey(KeySym keysym, unsigned int modifiers = 0);
private: private:
QString windowTitle(Window window, bool useBlacklist); QString windowTitle(Window window, bool useBlacklist);
@ -104,9 +104,9 @@ class AutoTypeExecutorX11 : public AutoTypeExecutor
public: public:
explicit AutoTypeExecutorX11(AutoTypePlatformX11* platform); explicit AutoTypeExecutorX11(AutoTypePlatformX11* platform);
void execBegin(const AutoTypeBegin* action) override; AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
void execType(const AutoTypeKey* action) override; AutoTypeAction::Result execType(const AutoTypeKey* action) override;
void execClearField(const AutoTypeClearField* action) override; AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
private: private:
AutoTypePlatformX11* const m_platform; AutoTypePlatformX11* const m_platform;