Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(libinputactions_SRCS
libinputactions/actions/CustomAction.cpp
libinputactions/actions/InputAction.cpp
libinputactions/actions/PlasmaGlobalShortcutAction.cpp
libinputactions/actions/ReplaceTextAction.cpp
libinputactions/actions/SleepAction.cpp
libinputactions/actions/TriggerAction.cpp
libinputactions/config/parsers/base.cpp
Expand All @@ -27,6 +28,7 @@ set(libinputactions_SRCS
libinputactions/config/GlobalConfig.cpp
libinputactions/config/Node.cpp
libinputactions/config/TextPosition.cpp
libinputactions/conditions/CanReplaceTextCondition.cpp
libinputactions/conditions/Condition.cpp
libinputactions/conditions/ConditionGroup.cpp
libinputactions/conditions/CustomCondition.cpp
Expand Down Expand Up @@ -75,7 +77,7 @@ set(libinputactions_SRCS
libinputactions/interfaces/PointerPositionSetter.h
libinputactions/interfaces/ProcessRunner.cpp
libinputactions/interfaces/SessionLock.h
libinputactions/interfaces/TextInput.h
libinputactions/interfaces/TextInput.cpp
libinputactions/interfaces/Window.h
libinputactions/interfaces/WindowProvider.cpp
libinputactions/triggers/core/DirectionalMotionTriggerCore.cpp
Expand Down
5 changes: 4 additions & 1 deletion src/libinputactions/InputActionsMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ void InputActionsMain::registerGlobalVariables(VariableManager *variableManager,
value = g_cursorShapeProvider->cursorShape();
});
variableManager->registerLocalVariable(BuiltinVariables::DeviceName);
for (auto i = 1; i <= s_fingerVariableCount; i++) {
for (auto i = 1; i <= FINGER_VARIABLE_COUNT; i++) {
variableManager->registerLocalVariable<QPointF>(QString("finger_%1_initial_position_percentage").arg(i));
variableManager->registerLocalVariable<QPointF>(QString("finger_%1_position_percentage").arg(i));
variableManager->registerLocalVariable<qreal>(QString("finger_%1_pressure").arg(i));
Expand All @@ -123,6 +123,9 @@ void InputActionsMain::registerGlobalVariables(VariableManager *variableManager,
variableManager->registerRemoteVariable<Qt::KeyboardModifiers>(BuiltinVariables::KeyboardModifiers, [](auto &value) {
value = g_inputBackend->keyboardModifiers();
});
for (auto i = 0; i < REGEX_MATCH_VARIABLE_COUNT; i++) {
variableManager->registerLocalVariable<QString>(QString("match_%1").arg(i));
}
variableManager->registerLocalVariable(BuiltinVariables::LastTriggerId);
variableManager->registerLocalVariable(BuiltinVariables::LastTriggerTimestamp, true);
variableManager->registerRemoteVariable<QPointF>("pointer_position_screen_percentage", [pointerPositionGetter](auto &value) {
Expand Down
66 changes: 66 additions & 0 deletions src/libinputactions/actions/ReplaceTextAction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

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 3 of the License, or
(at your option) any later version.

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 <https://www.gnu.org/licenses/>.
*/

#include "ReplaceTextAction.h"
#include <libinputactions/helpers/QThread.h>
#include <libinputactions/interfaces/TextInput.h>

namespace InputActions
{

ReplaceTextAction::ReplaceTextAction(std::vector<TextSubstitutionRule> rules)
: m_rules(std::move(rules))
{
}

void ReplaceTextAction::executeImpl(const ActionExecutionArguments &args)
{
TextSubstitutionRule *rule{};
QThreadHelpers::runOnThread(
QThreadHelpers::mainThread(),
[this, &rule] {
if (const auto it = std::ranges::find_if(m_rules,
[](const auto &rule) {
return g_textInput->canReplaceSurroundingText(rule.regex());
});
it != m_rules.end()) {
rule = &(*it);
}
},
true);
if (!rule) {
return;
}

g_textInput->replaceSurroundingText(rule->regex(), rule->newText());
}

bool ReplaceTextAction::async() const
{
return std::ranges::any_of(m_rules, [](const auto &rule) {
return g_textInput->canReplaceSurroundingText(rule.regex()) && rule.newText().expensive();
});
}

TextSubstitutionRule::TextSubstitutionRule(QRegularExpression regex, Value<QString> newText)
: m_regex(std::move(regex))
, m_newText(std::move(newText))
{
}

}
59 changes: 59 additions & 0 deletions src/libinputactions/actions/ReplaceTextAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

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 3 of the License, or
(at your option) any later version.

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 <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "Action.h"
#include <QString>
#include <libinputactions/Value.h>
#include <qregularexpression.h>

namespace InputActions
{

class TextSubstitutionRule
{
public:
TextSubstitutionRule() = default;
TextSubstitutionRule(QRegularExpression regex, Value<QString> newText);

const QRegularExpression &regex() const { return m_regex; }
const Value<QString> &newText() const { return m_newText; }

private:
QRegularExpression m_regex;
Value<QString> m_newText;
};

class ReplaceTextAction : public Action
{
public:
ReplaceTextAction(std::vector<TextSubstitutionRule> rules);

const std::vector<TextSubstitutionRule> &rules() const { return m_rules; }

bool async() const override;

protected:
void executeImpl(const ActionExecutionArguments &args) override;

private:
std::vector<TextSubstitutionRule> m_rules;
};

}
40 changes: 40 additions & 0 deletions src/libinputactions/conditions/CanReplaceTextCondition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

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 3 of the License, or
(at your option) any later version.

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 <https://www.gnu.org/licenses/>.
*/

#include "CanReplaceTextCondition.h"
#include <libinputactions/actions/ReplaceTextAction.h>
#include <libinputactions/interfaces/TextInput.h>

namespace InputActions
{

CanReplaceTextCondition::CanReplaceTextCondition(std::vector<TextSubstitutionRule> rules)
: m_rules(std::move(rules))
{
}

CanReplaceTextCondition::~CanReplaceTextCondition() = default;

bool CanReplaceTextCondition::evaluateImpl(const ConditionEvaluationArguments &arguments)
{
return std::ranges::any_of(m_rules, [](const auto &rule) {
return g_textInput->canReplaceSurroundingText(rule.regex());
});
}

}
43 changes: 43 additions & 0 deletions src/libinputactions/conditions/CanReplaceTextCondition.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

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 3 of the License, or
(at your option) any later version.

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 <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "Condition.h"

namespace InputActions
{

class TextSubstitutionRule;

class CanReplaceTextCondition : public Condition
{
public:
CanReplaceTextCondition(std::vector<TextSubstitutionRule> rules);
~CanReplaceTextCondition() override;

const std::vector<TextSubstitutionRule> &rules() const { return m_rules; }

protected:
bool evaluateImpl(const ConditionEvaluationArguments &arguments) override;

private:
std::vector<TextSubstitutionRule> m_rules;
};

}
16 changes: 16 additions & 0 deletions src/libinputactions/config/parsers/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@
#include <libinputactions/actions/CommandAction.h>
#include <libinputactions/actions/InputAction.h>
#include <libinputactions/actions/PlasmaGlobalShortcutAction.h>
#include <libinputactions/actions/ReplaceTextAction.h>
#include <libinputactions/actions/SleepAction.h>
#include <libinputactions/actions/TriggerAction.h>
#include <libinputactions/conditions/CanReplaceTextCondition.h>
#include <libinputactions/conditions/ConditionGroup.h>
#include <libinputactions/conditions/VariableCondition.h>
#include <libinputactions/config/ConfigIssue.h>
Expand Down Expand Up @@ -154,6 +156,8 @@ void NodeParser<std::unique_ptr<Action>>::parse(const Node *node, std::unique_pt
} else if (const auto *plasmaShortcutNode = node->at("plasma_shortcut")) {
const auto shortcut = parseSeparatedString2<QString>(plasmaShortcutNode, ',');
result = std::make_unique<PlasmaGlobalShortcutAction>(shortcut.first, shortcut.second);
} else if (const auto *replaceTextNode = node->at("replace_text")) {
result = std::make_unique<ReplaceTextAction>(replaceTextNode->as<std::vector<TextSubstitutionRule>>(true));
} else if (const auto *sleepActionNode = node->at("sleep")) {
result = std::make_unique<SleepAction>(sleepActionNode->as<std::chrono::milliseconds>());
} else if (const auto *oneNode = node->at("one")) {
Expand Down Expand Up @@ -218,6 +222,10 @@ std::shared_ptr<Condition> parseCondition(const Node *node, const VariableManage
return group;
}

if (const auto *canReplaceTextNode = node->at("can_replace_text")) {
return std::make_shared<CanReplaceTextCondition>(canReplaceTextNode->as<std::vector<TextSubstitutionRule>>(true));
}

if (isLegacy(node)) {
g_configIssueManager->addIssue(DeprecatedFeatureConfigIssue(node, DeprecatedFeature::LegacyConditions));

Expand Down Expand Up @@ -576,6 +584,14 @@ struct NodeParser<Range<T>>
};
template struct NodeParser<Range<qreal>>;

template<>
void NodeParser<TextSubstitutionRule>::parse(const Node *node, TextSubstitutionRule &result)
{
const auto regex = node->at("regex", true)->as<QRegularExpression>();
const auto newText = node->at("replace", true)->as<Value<QString>>();
result = {regex, newText};
}

template<>
void NodeParser<std::unique_ptr<TriggerAction>>::parse(const Node *node, std::unique_ptr<TriggerAction> &result)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void MultiTouchMotionTriggerHandler::updateVariables(const InputDevice *sender)
bool hasThumb{};

const auto touchPoints = sender ? sender->physicalState().validTouchPoints() : std::vector<const TouchPoint *>();
for (size_t i = 0; i < s_fingerVariableCount; i++) {
for (size_t i = 0; i < FINGER_VARIABLE_COUNT; i++) {
const auto fingerVariableNumber = i + 1;
auto initialPosition = g_variableManager->getVariable<QPointF>(QString("finger_%1_initial_position_percentage").arg(fingerVariableNumber));
auto position = g_variableManager->getVariable<QPointF>(QString("finger_%1_position_percentage").arg(fingerVariableNumber));
Expand Down
77 changes: 77 additions & 0 deletions src/libinputactions/interfaces/TextInput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

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 3 of the License, or
(at your option) any later version.

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 <https://www.gnu.org/licenses/>.
*/

#include "TextInput.h"
#include <libinputactions/helpers/QThread.h>
#include <libinputactions/variables/VariableManager.h>

namespace InputActions
{

bool TextInput::canReplaceSurroundingText(const QRegularExpression &regex)
{
const auto text = surroundingText();
if (!text) {
return false;
}

const auto match = regex.match(text.value());
if (!match.hasMatch()) {
return false;
}

const auto cursorPos = surroundingTextCursorPosition();
return !cursorPos || cursorPos == match.capturedEnd();
}

void TextInput::replaceSurroundingText(const QRegularExpression &regex, const Value<QString> &newText)
{
uint32_t capturedLength{};
QThreadHelpers::runOnThread(
QThreadHelpers::mainThread(),
[this, &regex, &capturedLength]() {
const auto match = regex.match(surroundingText().value());
capturedLength = match.capturedLength();

for (auto i = 0; i < REGEX_MATCH_VARIABLE_COUNT; i++) {
auto *variable = g_variableManager->getVariable(QString("match_%1").arg(i));
if (i > match.lastCapturedIndex()) {
variable->set({});
continue;
}

variable->set(match.capturedTexts()[i]);
}
},
true);

const auto text = newText.get();
if (!text) {
return;
}

QThreadHelpers::runOnThread(
QThreadHelpers::mainThread(),
[this, capturedLength, &text = text.value()]() {
deleteSurroundingText(capturedLength, 0);
writeText(text);
},
true);
}

}
Loading