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
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ set(libinputactions_SRCS
libinputactions/interfaces/CursorShapeProvider.h
libinputactions/interfaces/NotificationManager.cpp
libinputactions/interfaces/OnScreenMessageManager.h
libinputactions/interfaces/OverlayManager.cpp
libinputactions/interfaces/PointerPositionGetter.h
libinputactions/interfaces/PointerPositionSetter.h
libinputactions/interfaces/SessionLock.h
Expand Down
3 changes: 3 additions & 0 deletions src/libinputactions/InputActionsMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "interfaces/CursorShapeProvider.h"
#include "interfaces/NotificationManager.h"
#include "interfaces/OnScreenMessageManager.h"
#include "interfaces/OverlayManager.h"
#include "interfaces/PointerPositionGetter.h"
#include "interfaces/PointerPositionSetter.h"
#include "interfaces/SessionLock.h"
Expand All @@ -34,6 +35,7 @@ InputActionsMain::~InputActionsMain()
g_cursorShapeProvider.reset();
g_notificationManager.reset();
g_onScreenMessageManager.reset();
g_overlayManager.reset();
g_pointerPositionGetter.reset();
g_pointerPositionSetter.reset();
g_textInput.reset();
Expand Down Expand Up @@ -64,6 +66,7 @@ void InputActionsMain::setMissingImplementations()
setMissingImplementation(g_cursorShapeProvider);
setMissingImplementation(g_notificationManager);
setMissingImplementation(g_onScreenMessageManager);
setMissingImplementation(g_overlayManager);
setMissingImplementation(g_pointerPositionGetter);
setMissingImplementation(g_pointerPositionSetter);
setMissingImplementation(g_textInput);
Expand Down
10 changes: 10 additions & 0 deletions src/libinputactions/config/ConfigIssue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,16 @@ QString MissingRequiredPropertyConfigException::message() const
return QString("Required property '%1' was not specified.").arg(m_property);
}

OverlayProgramDoesNotExistConfigException::OverlayProgramDoesNotExistConfigException(const Node *node)
: ConfigException(node)
{
}

QString OverlayProgramDoesNotExistConfigException::message() const
{
return QString("Failed to find the inputactions-overlay program. It must be installed in one of the directories specified in the PATH environment variable.");
}

UncaughtScriptErrorConfigException::UncaughtScriptErrorConfigException(const Node *node, QJSValue error)
: ConfigException(node)
, m_message(ScriptingEngine::errorToString(error))
Expand Down
10 changes: 10 additions & 0 deletions src/libinputactions/config/ConfigIssue.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ class MissingRequiredPropertyConfigException
QString m_property;
};

class OverlayProgramDoesNotExistConfigException
: public ConfigException
, public virtual Copyable<OverlayProgramDoesNotExistConfigException, ConfigIssue>
{
public:
OverlayProgramDoesNotExistConfigException(const Node *node);

QString message() const override;
};

class UncaughtScriptErrorConfigException
: public ConfigException
, public virtual Copyable<UncaughtScriptErrorConfigException, ConfigIssue>
Expand Down
15 changes: 15 additions & 0 deletions src/libinputactions/config/ConfigLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <libinputactions/input/backends/LibevdevComplementaryInputBackend.h>
#include <libinputactions/input/devices/InputDeviceRule.h>
#include <libinputactions/interfaces/NotificationManager.h>
#include <libinputactions/interfaces/OverlayManager.h>
#include <libinputactions/scripting/ModuleScriptMetadata.h>
#include <libinputactions/scripting/ScriptingEngine.h>
#include <libinputactions/scripting/modules/core/Config.h>
Expand All @@ -65,6 +66,8 @@ struct ConfigData

std::vector<InputDeviceRule> deviceRules;
std::set<KeyboardKey> emergencyCombination = {KEY_BACKSPACE, KEY_SPACE, KEY_ENTER};

bool enableMouseStrokeOverlay{};
};

ConfigLoader::ConfigLoader()
Expand Down Expand Up @@ -256,6 +259,15 @@ QFuture<std::shared_ptr<ConfigData>> ConfigLoader::createConfig(const QString &r
config->touchscreenTriggerHandlerFactory(nullptr);
}

if (const auto *overlayNode = root->mapAt("overlay")) {
if (const auto *mouseStrokeNode = overlayNode->mapAt("mouse_stroke")) {
loadMember(config->enableMouseStrokeOverlay, mouseStrokeNode->at("enable"));
if (config->enableMouseStrokeOverlay && !g_overlayManager->overlayProgramExists()) {
throw OverlayProgramDoesNotExistConfigException(overlayNode);
}
}
}

root->at("anchors"); // Allow users to define anchors somewhere without unused property issues
root->addUnusedMapPropertyIssues();
return config;
Expand All @@ -273,6 +285,9 @@ QFuture<void> ConfigLoader::activateConfig(std::shared_ptr<ConfigData> config, b
auto *scriptingConfig = g_scriptingEngine->coreModule().config();

return scriptingConfig->aboutToBeActivatedSignal().emitAsync(false).then([initialize, scriptingConfig, config]() {
g_overlayManager->setEnabled(config->enableMouseStrokeOverlay);
g_overlayManager->setMouseStrokeOverlayEnabled(config->enableMouseStrokeOverlay);

g_globalConfig->setAllowExternalVariableAccess(config->allowExternalVariableAccess);
g_globalConfig->setAutoReload(config->autoReload);
g_globalConfig->setSendNotificationOnError(config->sendNotificationOnError);
Expand Down
6 changes: 5 additions & 1 deletion src/libinputactions/dbus/MainDBusInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <libinputactions/input/backends/InputBackend.h>
#include <libinputactions/input/devices/InputDevice.h>
#include <libinputactions/interfaces/OnScreenMessageManager.h>
#include <libinputactions/interfaces/OverlayManager.h>
#include <libinputactions/triggers/core/StrokeTriggerCore.h>
#include <libinputactions/variables/VariableRegistry.h>

Expand Down Expand Up @@ -67,16 +68,19 @@ void MainDBusInterface::recordStroke(const QDBusMessage &message)
return;
}

g_overlayManager->showMouseStrokeOverlay();
g_onScreenMessageManager->showMessage("InputActions is recording input. Perform a stroke gesture by moving the mouse or any amount of fingers all in the "
"same direction on a touchpad or a touchscreen. Recording will end after 250 ms of inactivity.");

message.setDelayedReply(true);
m_reply = message.createReply();

g_strokeRecorder->recordStroke([this](const auto &stroke) {
g_overlayManager->hideMouseStrokeOverlay();
g_onScreenMessageManager->hideMessage();

m_reply << strokeToBase64(stroke);
m_bus.send(m_reply);
g_onScreenMessageManager->hideMessage();
});
}

Expand Down
3 changes: 3 additions & 0 deletions src/libinputactions/handlers/MotionTriggerHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <libinputactions/helpers/Math.h>
#include <libinputactions/input/Delta.h>
#include <libinputactions/input/devices/InputDevice.h>
#include <libinputactions/interfaces/OverlayManager.h>
#include <libinputactions/triggers/core/StrokeTriggerCore.h>
#include <libinputactions/triggers/core/SwipeTriggerCore.h>

Expand Down Expand Up @@ -260,6 +261,8 @@ TriggerManagementOperationResult MotionTriggerHandler::endTriggersCustom(Trigger
return result;
}

g_overlayManager->hideMouseStrokeOverlay();

const Stroke stroke(m_deltas);
Trigger *best = nullptr;
double bestScore = 0;
Expand Down
11 changes: 11 additions & 0 deletions src/libinputactions/handlers/MouseTriggerHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <libinputactions/input/backends/InputBackend.h>
#include <libinputactions/input/devices/InputDevice.h>
#include <libinputactions/input/events.h>
#include <libinputactions/interfaces/OverlayManager.h>
#include <libinputactions/triggers/mouse/MousePressTrigger.h>
#include <libinputactions/triggers/mouse/MouseWheelTrigger.h>
#include <ranges>
Expand Down Expand Up @@ -220,6 +221,16 @@ void MouseTriggerHandler::triggerActivated(const Trigger *trigger)
MotionTriggerHandler::triggerActivated(trigger);
}

void MouseTriggerHandler::activeTriggersChanged()
{
const auto hasActiveStrokeTriggers = hasActiveTriggers(TriggerType::Stroke);
if (!hasActiveStrokeTriggers && g_overlayManager->mouseStrokeOverlayVisible()) {
g_overlayManager->hideMouseStrokeOverlay();
} else if (hasActiveStrokeTriggers && !g_overlayManager->mouseStrokeOverlayVisible()) {
g_overlayManager->showMouseStrokeOverlay();
}
}

std::unique_ptr<TriggerActivationEvent> MouseTriggerHandler::createActivationEvent() const
{
auto event = TriggerHandler::createActivationEvent();
Expand Down
1 change: 1 addition & 0 deletions src/libinputactions/handlers/MouseTriggerHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class MouseTriggerHandler : public MotionTriggerHandler
std::unique_ptr<TriggerActivationEvent> createActivationEvent() const override;

void triggerActivated(const Trigger *trigger) override;
void activeTriggersChanged() override;

private:
/**
Expand Down
5 changes: 5 additions & 0 deletions src/libinputactions/handlers/TriggerHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ TriggerManagementOperationResult TriggerHandler::activateTriggers(TriggerTypes t
for (auto &trigger : triggers(types, event)) {
triggerActivated(trigger);
m_activeTriggers.push_back(trigger);
activeTriggersChanged();

result.success = true;
result.block = result.block || trigger->blockEvents();
Expand Down Expand Up @@ -117,6 +118,7 @@ TriggerManagementOperationResult TriggerHandler::updateTriggers(const std::map<T
trigger->cancel();
}
it = m_activeTriggers.erase(it);
activeTriggersChanged();
continue;
}

Expand Down Expand Up @@ -169,6 +171,7 @@ TriggerManagementOperationResult TriggerHandler::doEndTriggers(TriggerTypes type
result.block = result.block || trigger->blockEvents();

it = m_activeTriggers.erase(it);
activeTriggersChanged();
if (!trigger->canEnd()) {
trigger->cancel();
continue;
Expand Down Expand Up @@ -215,6 +218,7 @@ TriggerManagementOperationResult TriggerHandler::doCancelTriggers(TriggerTypes t

trigger->cancel();
it = m_activeTriggers.erase(it);
activeTriggersChanged();
}
return result;
}
Expand All @@ -226,6 +230,7 @@ void TriggerHandler::cancelTriggers(Trigger *except)
if (gesture != except) {
gesture->cancel();
it = m_activeTriggers.erase(it);
activeTriggersChanged();
continue;
}
it++;
Expand Down
1 change: 1 addition & 0 deletions src/libinputactions/handlers/TriggerHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class TriggerHandler : public QObject

virtual void triggerActivated(const Trigger *trigger) {}
virtual void activatingTriggers(TriggerTypes types) {}
virtual void activeTriggersChanged() {}
virtual TriggerManagementOperationResult endTriggersCustom(TriggerTypes types) { return {}; }

/**
Expand Down
98 changes: 98 additions & 0 deletions src/libinputactions/interfaces/OverlayManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 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 "OverlayManager.h"
#include <QStandardPaths>
#include <libinputactions/helpers/QDBusConnection.h>

namespace InputActions
{

OverlayManager::OverlayManager()
: m_process(new QProcess)
{
m_process->setProgram("/usr/bin/env");
m_process->setArguments({"inputactions-overlay"});
m_process->setUnixProcessParameters(QProcess::UnixProcessFlag::ResetIds);
}

OverlayManager::~OverlayManager()
{
if (m_process->state() == QProcess::NotRunning) {
m_process->deleteLater();
} else {
QObject::connect(m_process, &QProcess::finished, [process = m_process]() {
process->deleteLater();
});
m_process->terminate();
}
}

void OverlayManager::setEnabled(bool value)
{
if (!value && m_process->state() != QProcess::NotRunning) {
m_process->terminate();
} else if (value && m_process->state() == QProcess::NotRunning) {
m_process->start();
}
}

bool OverlayManager::overlayProgramExists() const
{
return !QStandardPaths::findExecutable("inputactions-overlay").isEmpty();
}

void OverlayManager::showMouseStrokeOverlay()
{
if (auto *interface = mouseStrokeOverlayInterface()) {
interface->call("show");
m_mouseStrokeOverlayVisible = true;
}
}

void OverlayManager::hideMouseStrokeOverlay()
{
if (auto *interface = mouseStrokeOverlayInterface()) {
interface->call("hide");
m_mouseStrokeOverlayVisible = false;
}
}

QDBusInterface *OverlayManager::mouseStrokeOverlayInterface()
{
if (!m_mouseStrokeOverlayEnabled) {
return {};
}

if (m_mouseStrokeOverlayInterface) {
return &m_mouseStrokeOverlayInterface.value();
}

m_mouseStrokeOverlayInterface.emplace("org.inputactions.overlay",
"/org/inputactions/overlay/MouseStrokeOverlay",
"org.inputactions.overlay.MouseStrokeOverlay",
QDBusConnectionHelpers::sessionBus());
if (m_mouseStrokeOverlayInterface->isValid()) {
return &m_mouseStrokeOverlayInterface.value();
}

m_mouseStrokeOverlayInterface.reset();
return {};
}

}
60 changes: 60 additions & 0 deletions src/libinputactions/interfaces/OverlayManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 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 <QDBusInterface>
#include <QObject>
#include <QProcess>

namespace InputActions
{

class OverlayManager : public QObject
{
Q_OBJECT

public:
OverlayManager();
~OverlayManager() override;

void setEnabled(bool value);
void setMouseStrokeOverlayEnabled(bool value) { m_mouseStrokeOverlayEnabled = value; }

bool overlayProgramExists() const;

void showMouseStrokeOverlay();
void hideMouseStrokeOverlay();
bool mouseStrokeOverlayVisible() const { return m_mouseStrokeOverlayVisible; }

private:
/**
* Nullptr if not available.
*/
QDBusInterface *mouseStrokeOverlayInterface();

QProcess *m_process;

std::optional<QDBusInterface> m_mouseStrokeOverlayInterface;
bool m_mouseStrokeOverlayVisible{};
bool m_mouseStrokeOverlayEnabled{};
};

inline std::unique_ptr<OverlayManager> g_overlayManager;

}
Loading