mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-25 00:50:04 -05:00
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:
parent
4d07507739
commit
8c9530e3ec
@ -294,7 +294,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
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)) {
|
||||
if (m_plugin->activeWindow() != window) {
|
||||
@ -304,8 +305,25 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
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);
|
||||
|
||||
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;
|
||||
|
@ -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)
|
||||
@ -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) {
|
||||
// Change the delay between actions
|
||||
@ -52,14 +52,16 @@ void AutoTypeDelay::exec(AutoTypeExecutor* executor) const
|
||||
// Pause execution
|
||||
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);
|
||||
}
|
||||
|
@ -28,8 +28,61 @@ class AutoTypeExecutor;
|
||||
class KEEPASSXC_EXPORT AutoTypeAction
|
||||
{
|
||||
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;
|
||||
virtual void exec(AutoTypeExecutor* executor) const = 0;
|
||||
virtual Result exec(AutoTypeExecutor* executor) const = 0;
|
||||
virtual ~AutoTypeAction() = default;
|
||||
};
|
||||
|
||||
@ -38,7 +91,7 @@ class KEEPASSXC_EXPORT AutoTypeKey : public AutoTypeAction
|
||||
public:
|
||||
explicit AutoTypeKey(const QChar& character, 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 Qt::Key key = Qt::Key_unknown;
|
||||
@ -49,7 +102,7 @@ class KEEPASSXC_EXPORT AutoTypeDelay : public AutoTypeAction
|
||||
{
|
||||
public:
|
||||
explicit AutoTypeDelay(int delayMs, bool setExecDelay = false);
|
||||
void exec(AutoTypeExecutor* executor) const override;
|
||||
Result exec(AutoTypeExecutor* executor) const override;
|
||||
|
||||
const int delayMs;
|
||||
const bool setExecDelay;
|
||||
@ -58,24 +111,25 @@ public:
|
||||
class KEEPASSXC_EXPORT AutoTypeClearField : public AutoTypeAction
|
||||
{
|
||||
public:
|
||||
void exec(AutoTypeExecutor* executor) const override;
|
||||
Result exec(AutoTypeExecutor* executor) const override;
|
||||
};
|
||||
|
||||
class KEEPASSXC_EXPORT AutoTypeBegin : public AutoTypeAction
|
||||
{
|
||||
public:
|
||||
void exec(AutoTypeExecutor* executor) const override;
|
||||
Result exec(AutoTypeExecutor* executor) const override;
|
||||
};
|
||||
|
||||
class KEEPASSXC_EXPORT AutoTypeExecutor
|
||||
{
|
||||
public:
|
||||
virtual ~AutoTypeExecutor() = default;
|
||||
virtual void execBegin(const AutoTypeBegin* action) = 0;
|
||||
virtual void execType(const AutoTypeKey* action) = 0;
|
||||
virtual void execClearField(const AutoTypeClearField* action) = 0;
|
||||
virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
|
||||
virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
|
||||
virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;
|
||||
|
||||
int execDelayMs = 25;
|
||||
QString error;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_AUTOTYPEACTION_H
|
||||
|
@ -215,12 +215,13 @@ AutoTypeExecutorMac::AutoTypeExecutorMac(AutoTypePlatformMac* platform)
|
||||
{
|
||||
}
|
||||
|
||||
void AutoTypeExecutorMac::execBegin(const AutoTypeBegin* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorMac::execBegin(const AutoTypeBegin* 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) {
|
||||
m_platform->sendKey(Qt::Key_Shift, true);
|
||||
@ -251,12 +252,14 @@ void AutoTypeExecutorMac::execType(const AutoTypeKey* action)
|
||||
}
|
||||
|
||||
Tools::sleep(execDelayMs);
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
||||
void AutoTypeExecutorMac::execClearField(const AutoTypeClearField* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorMac::execClearField(const AutoTypeClearField* action)
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
execType(new AutoTypeKey(Qt::Key_Up, Qt::ControlModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_Down, Qt::ControlModifier | Qt::ShiftModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_Backspace));
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ class AutoTypeExecutorMac : public AutoTypeExecutor
|
||||
public:
|
||||
explicit AutoTypeExecutorMac(AutoTypePlatformMac* platform);
|
||||
|
||||
void execBegin(const AutoTypeBegin* action) override;
|
||||
void execType(const AutoTypeKey* action) override;
|
||||
void execClearField(const AutoTypeClearField* action) override;
|
||||
AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
|
||||
AutoTypeAction::Result execType(const AutoTypeKey* action) override;
|
||||
AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
|
||||
|
||||
private:
|
||||
AutoTypePlatformMac* const m_platform;
|
||||
|
@ -102,17 +102,20 @@ AutoTypeExecutorTest::AutoTypeExecutorTest(AutoTypePlatformTest* platform)
|
||||
{
|
||||
}
|
||||
|
||||
void AutoTypeExecutorTest::execBegin(const AutoTypeBegin* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorTest::execBegin(const AutoTypeBegin* 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);
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
||||
void AutoTypeExecutorTest::execClearField(const AutoTypeClearField* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorTest::execClearField(const AutoTypeClearField* action)
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ class AutoTypeExecutorTest : public AutoTypeExecutor
|
||||
public:
|
||||
explicit AutoTypeExecutorTest(AutoTypePlatformTest* platform);
|
||||
|
||||
void execBegin(const AutoTypeBegin* action) override;
|
||||
void execType(const AutoTypeKey* action) override;
|
||||
void execClearField(const AutoTypeClearField* action) override;
|
||||
AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
|
||||
AutoTypeAction::Result execType(const AutoTypeKey* action) override;
|
||||
AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
|
||||
|
||||
private:
|
||||
AutoTypePlatformTest* const m_platform;
|
||||
|
@ -226,12 +226,13 @@ AutoTypeExecutorWin::AutoTypeExecutorWin(AutoTypePlatformWin* platform)
|
||||
{
|
||||
}
|
||||
|
||||
void AutoTypeExecutorWin::execBegin(const AutoTypeBegin* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorWin::execBegin(const AutoTypeBegin* 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) {
|
||||
m_platform->sendKey(Qt::Key_Shift, true);
|
||||
@ -262,12 +263,14 @@ void AutoTypeExecutorWin::execType(const AutoTypeKey* action)
|
||||
}
|
||||
|
||||
Tools::sleep(execDelayMs);
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
||||
void AutoTypeExecutorWin::execClearField(const AutoTypeClearField* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorWin::execClearField(const AutoTypeClearField* action)
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_Backspace));
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
@ -54,9 +54,9 @@ class AutoTypeExecutorWin : public AutoTypeExecutor
|
||||
public:
|
||||
explicit AutoTypeExecutorWin(AutoTypePlatformWin* platform);
|
||||
|
||||
void execBegin(const AutoTypeBegin* action) override;
|
||||
void execType(const AutoTypeKey* action) override;
|
||||
void execClearField(const AutoTypeClearField* action) override;
|
||||
AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
|
||||
AutoTypeAction::Result execType(const AutoTypeKey* action) override;
|
||||
AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
|
||||
|
||||
private:
|
||||
AutoTypePlatformWin* const m_platform;
|
||||
|
@ -395,11 +395,10 @@ bool AutoTypePlatformX11::GetKeycode(KeySym keysym, int* keycode, int* group, un
|
||||
* window to simulate keyboard. If modifiers (shift, control, etc)
|
||||
* are set ON, many events will be sent.
|
||||
*/
|
||||
void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
|
||||
AutoTypeAction::Result AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
|
||||
{
|
||||
if (keysym == NoSymbol) {
|
||||
qWarning("No such key: keysym=0x%lX", keysym);
|
||||
return;
|
||||
return AutoTypeAction::Result::Failed(tr("Trying to send invalid keysym."));
|
||||
}
|
||||
|
||||
int keycode;
|
||||
@ -417,8 +416,8 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
|
||||
|
||||
/* determine keycode, group and mask for the given keysym */
|
||||
if (!GetKeycode(keysym, &keycode, &group, &wanted_mask)) {
|
||||
qWarning("Unable to get valid keycode for key: keysym=0x%lX", keysym);
|
||||
return;
|
||||
return AutoTypeAction::Result::Failed(tr("Unable to get valid keycode for key: ")
|
||||
+ QString(XKeysymToString(keysym)));
|
||||
}
|
||||
|
||||
wanted_mask |= modifiers;
|
||||
@ -498,6 +497,8 @@ void AutoTypePlatformX11::sendKey(KeySym keysym, unsigned int modifiers)
|
||||
XkbLockGroup(m_dpy, XkbUseCoreKbd, group_active);
|
||||
XFlush(m_dpy);
|
||||
}
|
||||
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
||||
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);
|
||||
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) {
|
||||
m_platform->sendKey(qtToNativeKeyCode(action->key), qtToNativeModifiers(action->modifiers));
|
||||
result = m_platform->sendKey(qtToNativeKeyCode(action->key), qtToNativeModifiers(action->modifiers));
|
||||
} 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);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void AutoTypeExecutorX11::execClearField(const AutoTypeClearField* action)
|
||||
AutoTypeAction::Result AutoTypeExecutorX11::execClearField(const AutoTypeClearField* action)
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
execType(new AutoTypeKey(Qt::Key_Home, Qt::ControlModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_End, Qt::ControlModifier | Qt::ShiftModifier));
|
||||
execType(new AutoTypeKey(Qt::Key_Backspace));
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
||||
bool AutoTypePlatformX11::raiseWindow(WId window)
|
||||
|
@ -56,7 +56,7 @@ public:
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
void updateKeymap();
|
||||
|
||||
void sendKey(KeySym keysym, unsigned int modifiers = 0);
|
||||
AutoTypeAction::Result sendKey(KeySym keysym, unsigned int modifiers = 0);
|
||||
|
||||
private:
|
||||
QString windowTitle(Window window, bool useBlacklist);
|
||||
@ -104,9 +104,9 @@ class AutoTypeExecutorX11 : public AutoTypeExecutor
|
||||
public:
|
||||
explicit AutoTypeExecutorX11(AutoTypePlatformX11* platform);
|
||||
|
||||
void execBegin(const AutoTypeBegin* action) override;
|
||||
void execType(const AutoTypeKey* action) override;
|
||||
void execClearField(const AutoTypeClearField* action) override;
|
||||
AutoTypeAction::Result execBegin(const AutoTypeBegin* action) override;
|
||||
AutoTypeAction::Result execType(const AutoTypeKey* action) override;
|
||||
AutoTypeAction::Result execClearField(const AutoTypeClearField* action) override;
|
||||
|
||||
private:
|
||||
AutoTypePlatformX11* const m_platform;
|
||||
|
Loading…
x
Reference in New Issue
Block a user