mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Several fixes for Auto-Type
* On Windows, offer explicit methods to use the virtual keyboard style of typing. This partially reverts 1150b69836
by going back to the standard unicode method by default. However, uses can either add {MODE=VIRTUAL} to their sequence or choose "Use Virtual Keyboard" / CTRL+4 from the selection dialog.
* Took this opportunity to clean up the signature of AutoType::performAutoType and AutoType::performAutoTypeWithSequence by removing the "hideWindow" attribute.
* Show keyboard shortcuts on the selection dialog context menu
* Fix selection dialog help icon color when in dark theme
This commit is contained in:
parent
392cab2e36
commit
8a7eb36950
Binary file not shown.
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 61 KiB |
@ -60,6 +60,7 @@ image::autotype_entry_sequences.png[]
|
||||
|{DELAY X} |Pause typing for X milliseconds
|
||||
|{CLEARFIELD} |Clear the input field
|
||||
|{PICKCHARS} |Pick specific password characters from a dialog
|
||||
|{MODE=VIRTUAL} |(Experimental) Use virtual key presses on Windows, useful for virtual machines
|
||||
|===
|
||||
|
||||
=== Performing Global Auto-Type
|
||||
|
@ -705,14 +705,6 @@
|
||||
<source>Double click a row to perform Auto-Type or find an entry using the search:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
||||
Ctrl+F - Toggle database search<br/>
|
||||
Ctrl+1 - Type username<br/>
|
||||
Ctrl+2 - Type password<br/>
|
||||
Ctrl+3 - Type TOTP</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Search all open databases</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -753,6 +745,19 @@ Ctrl+3 - Type TOTP</p></source>
|
||||
<source>Copy TOTP</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
||||
Ctrl+F - Toggle database search<br/>
|
||||
Ctrl+1 - Type username<br/>
|
||||
Ctrl+2 - Type password<br/>
|
||||
Ctrl+3 - Type TOTP<br/>
|
||||
Ctrl+4 - Use Virtual Keyboard (Windows Only)</p></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Use Virtual Keyboard</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BrowserAccessControlDialog</name>
|
||||
|
@ -254,7 +254,10 @@ void AutoType::unregisterGlobalShortcut()
|
||||
/**
|
||||
* Core Autotype function that will execute actions
|
||||
*/
|
||||
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
|
||||
void AutoType::executeAutoTypeActions(const Entry* entry,
|
||||
const QString& sequence,
|
||||
WId window,
|
||||
AutoTypeExecutor::Mode mode)
|
||||
{
|
||||
QString error;
|
||||
auto actions = parseSequence(sequence, entry, error);
|
||||
@ -274,7 +277,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
return;
|
||||
}
|
||||
|
||||
if (hideWindow) {
|
||||
// Explicitly hide the main window if no target window is specified
|
||||
if (window == 0) {
|
||||
#if defined(Q_OS_MACOS)
|
||||
// Check for accessibility permission
|
||||
if (!macUtils()->enableAccessibility()) {
|
||||
@ -289,20 +293,22 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
macUtils()->raiseLastActiveWindow();
|
||||
m_plugin->hideOwnWindow();
|
||||
#else
|
||||
if (getMainWindow()) {
|
||||
getMainWindow()->minimizeOrHide();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Restore window state in case app stole focus
|
||||
#endif
|
||||
QCoreApplication::processEvents();
|
||||
window = m_plugin->activeWindow();
|
||||
} else {
|
||||
// Restore window state (macOS only) then raise the target window
|
||||
restoreWindowState();
|
||||
QCoreApplication::processEvents();
|
||||
m_plugin->raiseWindow(m_windowForGlobal);
|
||||
|
||||
// Used only for selected entry auto-type
|
||||
if (!window) {
|
||||
window = m_plugin->activeWindow();
|
||||
m_plugin->raiseWindow(window);
|
||||
}
|
||||
|
||||
// Restore executor mode
|
||||
m_executor->mode = mode;
|
||||
|
||||
int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
|
||||
Tools::wait(delay);
|
||||
|
||||
@ -346,7 +352,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
||||
* Single Autotype entry-point function
|
||||
* Look up the Auto-Type sequence for the given entry then perfom Auto-Type in the active window
|
||||
*/
|
||||
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
void AutoType::performAutoType(const Entry* entry)
|
||||
{
|
||||
if (!m_plugin) {
|
||||
return;
|
||||
@ -354,7 +360,7 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
|
||||
auto sequences = entry->autoTypeSequences();
|
||||
if (!sequences.isEmpty()) {
|
||||
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
||||
executeAutoTypeActions(entry, sequences.first());
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,13 +368,13 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
||||
* Extra Autotype entry-point function
|
||||
* Perfom Auto-Type of the directly specified sequence in the active window
|
||||
*/
|
||||
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow)
|
||||
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence)
|
||||
{
|
||||
if (!m_plugin) {
|
||||
return;
|
||||
}
|
||||
|
||||
executeAutoTypeActions(entry, hideWindow, sequence);
|
||||
executeAutoTypeActions(entry, sequence);
|
||||
}
|
||||
|
||||
void AutoType::startGlobalAutoType(const QString& search)
|
||||
@ -467,10 +473,17 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
||||
}
|
||||
|
||||
connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
|
||||
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](const AutoTypeMatch& match) {
|
||||
connect(selectDialog,
|
||||
&AutoTypeSelectDialog::matchActivated,
|
||||
this,
|
||||
[this](const AutoTypeMatch& match, bool virtualMode) {
|
||||
m_lastMatch = match;
|
||||
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
|
||||
executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal);
|
||||
executeAutoTypeActions(match.first,
|
||||
match.second,
|
||||
m_windowForGlobal,
|
||||
virtualMode ? AutoTypeExecutor::Mode::VIRTUAL
|
||||
: AutoTypeExecutor::Mode::NORMAL);
|
||||
resetAutoTypeState();
|
||||
});
|
||||
connect(selectDialog, &QDialog::rejected, this, [this] {
|
||||
@ -488,7 +501,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
||||
selectDialog->activateWindow();
|
||||
} else if (!matchList.isEmpty()) {
|
||||
// Only one match and not asking, do it!
|
||||
executeAutoTypeActions(matchList.first().first, nullptr, matchList.first().second, m_windowForGlobal);
|
||||
executeAutoTypeActions(matchList.first().first, matchList.first().second, m_windowForGlobal);
|
||||
resetAutoTypeState();
|
||||
} else {
|
||||
// We should never get here
|
||||
@ -717,6 +730,12 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
|
||||
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||
return {};
|
||||
}
|
||||
} else if (placeholder.startsWith("mode=")) {
|
||||
auto mode = AutoTypeExecutor::Mode::NORMAL;
|
||||
if (placeholder.endsWith("virtual")) {
|
||||
mode = AutoTypeExecutor::Mode::VIRTUAL;
|
||||
}
|
||||
actions << QSharedPointer<AutoTypeMode>::create(mode);
|
||||
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|
||||
|| placeholder.startsWith("c:")) {
|
||||
// Ignore these commands
|
||||
|
@ -19,15 +19,15 @@
|
||||
#ifndef KEEPASSX_AUTOTYPE_H
|
||||
#define KEEPASSX_AUTOTYPE_H
|
||||
|
||||
#include "AutoTypeAction.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include "AutoTypeAction.h"
|
||||
#include "AutoTypeMatch.h"
|
||||
|
||||
class AutoTypeAction;
|
||||
class AutoTypeExecutor;
|
||||
class AutoTypePlatformInterface;
|
||||
class Database;
|
||||
class Entry;
|
||||
@ -41,8 +41,8 @@ public:
|
||||
QStringList windowTitles();
|
||||
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
|
||||
void unregisterGlobalShortcut();
|
||||
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
|
||||
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);
|
||||
void performAutoType(const Entry* entry);
|
||||
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence);
|
||||
|
||||
static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);
|
||||
|
||||
@ -80,9 +80,9 @@ private:
|
||||
~AutoType() override;
|
||||
void loadPlugin(const QString& pluginPath);
|
||||
void executeAutoTypeActions(const Entry* entry,
|
||||
QWidget* hideWindow = nullptr,
|
||||
const QString& customSequence = QString(),
|
||||
WId window = 0);
|
||||
const QString& sequence = QString(),
|
||||
WId window = 0,
|
||||
AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
|
||||
void restoreWindowState();
|
||||
void resetAutoTypeState();
|
||||
|
||||
|
@ -65,3 +65,14 @@ AutoTypeAction::Result AutoTypeBegin::exec(AutoTypeExecutor* executor) const
|
||||
{
|
||||
return executor->execBegin(this);
|
||||
}
|
||||
|
||||
AutoTypeMode::AutoTypeMode(AutoTypeExecutor::Mode mode)
|
||||
: mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
AutoTypeAction::Result AutoTypeMode::exec(AutoTypeExecutor* executor) const
|
||||
{
|
||||
executor->mode = mode;
|
||||
return AutoTypeAction::Result::Ok();
|
||||
}
|
||||
|
@ -121,13 +121,29 @@ public:
|
||||
class KEEPASSXC_EXPORT AutoTypeExecutor
|
||||
{
|
||||
public:
|
||||
enum class Mode
|
||||
{
|
||||
NORMAL,
|
||||
VIRTUAL
|
||||
};
|
||||
|
||||
virtual ~AutoTypeExecutor() = default;
|
||||
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;
|
||||
Mode mode = Mode::NORMAL;
|
||||
QString error;
|
||||
};
|
||||
|
||||
class KEEPASSXC_EXPORT AutoTypeMode : public AutoTypeAction
|
||||
{
|
||||
public:
|
||||
AutoTypeMode(AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
|
||||
Result exec(AutoTypeExecutor* executor) const override;
|
||||
|
||||
const AutoTypeExecutor::Mode mode;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_AUTOTYPEACTION_H
|
||||
|
@ -58,6 +58,8 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
|
||||
}
|
||||
});
|
||||
|
||||
m_ui->helpButton->setIcon(icons()->icon("system-help"));
|
||||
|
||||
m_ui->search->installEventFilter(this);
|
||||
|
||||
m_searchTimer.setInterval(300);
|
||||
@ -118,7 +120,7 @@ void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match)
|
||||
if (match.first) {
|
||||
m_accepted = true;
|
||||
accept();
|
||||
emit matchActivated(std::move(match));
|
||||
emit matchActivated(std::move(match), m_virtualMode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,34 +276,54 @@ void AutoTypeSelectDialog::buildActionMenu()
|
||||
m_actionMenu->addAction(typeUsernameAction);
|
||||
m_actionMenu->addAction(typePasswordAction);
|
||||
m_actionMenu->addAction(typeTotpAction);
|
||||
#ifdef Q_OS_WIN
|
||||
auto typeVirtualAction = new QAction(icons()->icon("auto-type"), tr("Use Virtual Keyboard"));
|
||||
m_actionMenu->addAction(typeVirtualAction);
|
||||
#endif
|
||||
m_actionMenu->addAction(copyUsernameAction);
|
||||
m_actionMenu->addAction(copyPasswordAction);
|
||||
m_actionMenu->addAction(copyTotpAction);
|
||||
|
||||
auto shortcut = new QShortcut(Qt::CTRL + Qt::Key_1, this);
|
||||
connect(shortcut, &QShortcut::activated, typeUsernameAction, &QAction::trigger);
|
||||
typeUsernameAction->setShortcut(Qt::CTRL + Qt::Key_1);
|
||||
connect(typeUsernameAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{USERNAME}";
|
||||
submitAutoTypeMatch(match);
|
||||
});
|
||||
|
||||
shortcut = new QShortcut(Qt::CTRL + Qt::Key_2, this);
|
||||
connect(shortcut, &QShortcut::activated, typePasswordAction, &QAction::trigger);
|
||||
typePasswordAction->setShortcut(Qt::CTRL + Qt::Key_2);
|
||||
connect(typePasswordAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{PASSWORD}";
|
||||
submitAutoTypeMatch(match);
|
||||
});
|
||||
|
||||
shortcut = new QShortcut(Qt::CTRL + Qt::Key_3, this);
|
||||
connect(shortcut, &QShortcut::activated, typeTotpAction, &QAction::trigger);
|
||||
typeTotpAction->setShortcut(Qt::CTRL + Qt::Key_3);
|
||||
connect(typeTotpAction, &QAction::triggered, this, [&] {
|
||||
auto match = m_ui->view->currentMatch();
|
||||
match.second = "{TOTP}";
|
||||
submitAutoTypeMatch(match);
|
||||
});
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
typeVirtualAction->setShortcut(Qt::CTRL + Qt::Key_4);
|
||||
connect(typeVirtualAction, &QAction::triggered, this, [&] {
|
||||
m_virtualMode = true;
|
||||
activateCurrentMatch();
|
||||
});
|
||||
#endif
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
|
||||
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
|
||||
typeUsernameAction->setShortcutVisibleInContextMenu(true);
|
||||
typePasswordAction->setShortcutVisibleInContextMenu(true);
|
||||
typeTotpAction->setShortcutVisibleInContextMenu(true);
|
||||
#ifdef Q_OS_WIN
|
||||
typeVirtualAction->setShortcutVisibleInContextMenu(true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
connect(copyUsernameAction, &QAction::triggered, this, [&] {
|
||||
auto entry = m_ui->view->currentMatch().first;
|
||||
if (entry) {
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
void setSearchString(const QString& search);
|
||||
|
||||
signals:
|
||||
void matchActivated(AutoTypeMatch match);
|
||||
void matchActivated(AutoTypeMatch match, bool virtualMode = false);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
@ -69,6 +69,7 @@ private:
|
||||
QTimer m_searchTimer;
|
||||
QPointer<QMenu> m_actionMenu;
|
||||
|
||||
bool m_virtualMode = false;
|
||||
bool m_accepted = false;
|
||||
};
|
||||
|
||||
|
@ -43,34 +43,29 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QToolButton" name="helpButton">
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>14</width>
|
||||
<height>14</height>
|
||||
</size>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><p>You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:<br/>
|
||||
Ctrl+F - Toggle database search<br/>
|
||||
Ctrl+1 - Type username<br/>
|
||||
Ctrl+2 - Type password<br/>
|
||||
Ctrl+3 - Type TOTP</p></string>
|
||||
Ctrl+3 - Type TOTP<br/>
|
||||
Ctrl+4 - Use Virtual Keyboard (Windows Only)</p></string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolButton {
|
||||
border: none;
|
||||
background: none;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../share/icons/icons.qrc">:/icons/application/scalable/actions/system-help.svg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
<string notr="true">?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -81,67 +81,71 @@ bool AutoTypePlatformWin::raiseWindow(WId window)
|
||||
//
|
||||
// Send unicode character to foreground window
|
||||
//
|
||||
void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
||||
void AutoTypePlatformWin::sendChar(const QChar& ch)
|
||||
{
|
||||
auto vkey = VkKeyScanExW(ch.unicode(), GetKeyboardLayout(0));
|
||||
if (vkey == -1) {
|
||||
// VKey not found, send as Unicode character
|
||||
DWORD flags = KEYEVENTF_UNICODE;
|
||||
if (!isKeyDown) {
|
||||
flags |= KEYEVENTF_KEYUP;
|
||||
DWORD nativeFlags = KEYEVENTF_UNICODE;
|
||||
|
||||
INPUT in[2];
|
||||
in[0].type = INPUT_KEYBOARD;
|
||||
in[0].ki.wVk = 0;
|
||||
in[0].ki.wScan = ch.unicode();
|
||||
in[0].ki.dwFlags = KEYEVENTF_UNICODE;
|
||||
in[0].ki.time = 0;
|
||||
in[0].ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||
|
||||
in[1] = in[0];
|
||||
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
::SendInput(2, &in[0], sizeof(INPUT));
|
||||
}
|
||||
|
||||
INPUT in;
|
||||
in.type = INPUT_KEYBOARD;
|
||||
in.ki.wVk = 0;
|
||||
in.ki.wScan = ch.unicode();
|
||||
in.ki.dwFlags = flags;
|
||||
in.ki.time = 0;
|
||||
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||
::SendInput(1, &in, sizeof(INPUT));
|
||||
void AutoTypePlatformWin::sendCharVirtual(const QChar& ch)
|
||||
{
|
||||
auto vKey = VkKeyScanExW(ch.unicode(), GetKeyboardLayout(0));
|
||||
if (vKey == -1) {
|
||||
// VKey not found, send as Unicode character
|
||||
sendChar(ch);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HIBYTE(vkey) & 0x6) {
|
||||
sendKey(Qt::Key_AltGr, true);
|
||||
if (HIBYTE(vKey) & 0x6) {
|
||||
setKeyState(Qt::Key_AltGr, true);
|
||||
} else {
|
||||
if (HIBYTE(vkey) & 0x1) {
|
||||
sendKey(Qt::Key_Shift, true);
|
||||
if (HIBYTE(vKey) & 0x1) {
|
||||
setKeyState(Qt::Key_Shift, true);
|
||||
}
|
||||
if (HIBYTE(vkey) & 0x2) {
|
||||
sendKey(Qt::Key_Control, true);
|
||||
if (HIBYTE(vKey) & 0x2) {
|
||||
setKeyState(Qt::Key_Control, true);
|
||||
}
|
||||
if (HIBYTE(vkey) & 0x4) {
|
||||
sendKey(Qt::Key_Alt, true);
|
||||
if (HIBYTE(vKey) & 0x4) {
|
||||
setKeyState(Qt::Key_Alt, true);
|
||||
}
|
||||
}
|
||||
|
||||
DWORD flags = KEYEVENTF_SCANCODE;
|
||||
if (!isKeyDown) {
|
||||
flags |= KEYEVENTF_KEYUP;
|
||||
}
|
||||
INPUT in[2];
|
||||
in[0].type = INPUT_KEYBOARD;
|
||||
in[0].ki.wVk = 0;
|
||||
in[0].ki.wScan = MapVirtualKey(LOBYTE(vKey), MAPVK_VK_TO_VSC);
|
||||
in[0].ki.dwFlags = KEYEVENTF_SCANCODE;
|
||||
in[0].ki.time = 0;
|
||||
in[0].ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||
|
||||
INPUT in;
|
||||
in.type = INPUT_KEYBOARD;
|
||||
in.ki.wVk = 0;
|
||||
in.ki.wScan = MapVirtualKey(LOBYTE(vkey), MAPVK_VK_TO_VSC);
|
||||
in.ki.dwFlags = flags;
|
||||
in.ki.time = 0;
|
||||
in.ki.dwExtraInfo = ::GetMessageExtraInfo();
|
||||
in[1] = in[0];
|
||||
in[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
::SendInput(1, &in, sizeof(INPUT));
|
||||
::SendInput(2, &in[0], sizeof(INPUT));
|
||||
|
||||
if (HIBYTE(vkey) & 0x6) {
|
||||
sendKey(Qt::Key_AltGr, false);
|
||||
if (HIBYTE(vKey) & 0x6) {
|
||||
setKeyState(Qt::Key_AltGr, false);
|
||||
} else {
|
||||
if (HIBYTE(vkey) & 0x1) {
|
||||
sendKey(Qt::Key_Shift, false);
|
||||
if (HIBYTE(vKey) & 0x1) {
|
||||
setKeyState(Qt::Key_Shift, false);
|
||||
}
|
||||
if (HIBYTE(vkey) & 0x2) {
|
||||
sendKey(Qt::Key_Control, false);
|
||||
if (HIBYTE(vKey) & 0x2) {
|
||||
setKeyState(Qt::Key_Control, false);
|
||||
}
|
||||
if (HIBYTE(vkey) & 0x4) {
|
||||
sendKey(Qt::Key_Alt, false);
|
||||
if (HIBYTE(vKey) & 0x4) {
|
||||
setKeyState(Qt::Key_Alt, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,14 +153,14 @@ void AutoTypePlatformWin::sendChar(const QChar& ch, bool isKeyDown)
|
||||
//
|
||||
// Send virtual key code to foreground window
|
||||
//
|
||||
void AutoTypePlatformWin::sendKey(Qt::Key key, bool isKeyDown)
|
||||
void AutoTypePlatformWin::setKeyState(Qt::Key key, bool down)
|
||||
{
|
||||
WORD nativeKeyCode = winUtils()->qtToNativeKeyCode(key);
|
||||
DWORD nativeFlags = KEYEVENTF_SCANCODE;
|
||||
if (isExtendedKey(nativeKeyCode)) {
|
||||
nativeFlags |= KEYEVENTF_EXTENDEDKEY;
|
||||
}
|
||||
if (!isKeyDown) {
|
||||
if (!down) {
|
||||
nativeFlags |= KEYEVENTF_KEYUP;
|
||||
}
|
||||
|
||||
@ -279,37 +283,40 @@ AutoTypeAction::Result AutoTypeExecutorWin::execBegin(const AutoTypeBegin* actio
|
||||
AutoTypeAction::Result AutoTypeExecutorWin::execType(const AutoTypeKey* action)
|
||||
{
|
||||
if (action->modifiers & Qt::ShiftModifier) {
|
||||
m_platform->sendKey(Qt::Key_Shift, true);
|
||||
m_platform->setKeyState(Qt::Key_Shift, true);
|
||||
}
|
||||
if (action->modifiers & Qt::ControlModifier) {
|
||||
m_platform->sendKey(Qt::Key_Control, true);
|
||||
m_platform->setKeyState(Qt::Key_Control, true);
|
||||
}
|
||||
if (action->modifiers & Qt::AltModifier) {
|
||||
m_platform->sendKey(Qt::Key_Alt, true);
|
||||
m_platform->setKeyState(Qt::Key_Alt, true);
|
||||
}
|
||||
if (action->modifiers & Qt::MetaModifier) {
|
||||
m_platform->sendKey(Qt::Key_Meta, true);
|
||||
m_platform->setKeyState(Qt::Key_Meta, true);
|
||||
}
|
||||
|
||||
if (action->key != Qt::Key_unknown) {
|
||||
m_platform->sendKey(action->key, true);
|
||||
m_platform->sendKey(action->key, false);
|
||||
m_platform->setKeyState(action->key, true);
|
||||
m_platform->setKeyState(action->key, false);
|
||||
} else {
|
||||
m_platform->sendChar(action->character, true);
|
||||
m_platform->sendChar(action->character, false);
|
||||
if (mode == Mode::VIRTUAL) {
|
||||
m_platform->sendCharVirtual(action->character);
|
||||
} else {
|
||||
m_platform->sendChar(action->character);
|
||||
}
|
||||
}
|
||||
|
||||
if (action->modifiers & Qt::ShiftModifier) {
|
||||
m_platform->sendKey(Qt::Key_Shift, false);
|
||||
m_platform->setKeyState(Qt::Key_Shift, false);
|
||||
}
|
||||
if (action->modifiers & Qt::ControlModifier) {
|
||||
m_platform->sendKey(Qt::Key_Control, false);
|
||||
m_platform->setKeyState(Qt::Key_Control, false);
|
||||
}
|
||||
if (action->modifiers & Qt::AltModifier) {
|
||||
m_platform->sendKey(Qt::Key_Alt, false);
|
||||
m_platform->setKeyState(Qt::Key_Alt, false);
|
||||
}
|
||||
if (action->modifiers & Qt::MetaModifier) {
|
||||
m_platform->sendKey(Qt::Key_Meta, false);
|
||||
m_platform->setKeyState(Qt::Key_Meta, false);
|
||||
}
|
||||
|
||||
Tools::sleep(execDelayMs);
|
||||
|
@ -43,8 +43,9 @@ public:
|
||||
bool raiseWindow(WId window) override;
|
||||
AutoTypeExecutor* createExecutor() override;
|
||||
|
||||
void sendChar(const QChar& ch, bool isKeyDown);
|
||||
void sendKey(Qt::Key key, bool isKeyDown);
|
||||
void sendCharVirtual(const QChar& ch);
|
||||
void sendChar(const QChar& ch);
|
||||
void setKeyState(Qt::Key key, bool down);
|
||||
|
||||
private:
|
||||
static bool isExtendedKey(DWORD nativeKeyCode);
|
||||
|
@ -782,9 +782,9 @@ void DatabaseWidget::performAutoType(const QString& sequence)
|
||||
}
|
||||
|
||||
if (sequence.isEmpty()) {
|
||||
autoType()->performAutoType(currentEntry, window());
|
||||
autoType()->performAutoType(currentEntry);
|
||||
} else {
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, sequence, window());
|
||||
autoType()->performAutoTypeWithSequence(currentEntry, sequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ void TestAutoType::testInternal()
|
||||
|
||||
void TestAutoType::testSingleAutoType()
|
||||
{
|
||||
m_autoType->performAutoType(m_entry1, nullptr);
|
||||
m_autoType->performAutoType(m_entry1);
|
||||
|
||||
QCOMPARE(m_test->actionCount(), 14);
|
||||
QCOMPARE(m_test->actionChars(),
|
||||
|
Loading…
Reference in New Issue
Block a user