commit 76dfebe0e8fb5d834a6b0f0d35690098297f0596 Author: Joshua Goins Date: Thu Nov 7 22:34:41 2024 -0500 Add initial files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c4f1f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: None +# SPDX-License-Identifier: CC0-1.0 + +.clang-format +/build*/ +CMakeLists.txt.user +compile_commands.json +.cache +src/resources.generated.qrc +.flatpak-builder/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..99ec49b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.30) +project(inputfaker) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(QT_MIN_VERSION 6.5) +set(KF_MIN_VERSION 6.0) + +find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE) + +list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(KDEInstallDirs) +include(ECMFindQmlModule) +include(KDECMakeSettings) +include(KDECompilerSettings NO_POLICY_SCOPE) +include(ECMSetupVersion) +include(ECMGenerateHeaders) +include(ECMPoQmTools) +include(KDEGitCommitHooks) +include(KDEClangFormat) +include(ECMQmlModule) + +find_package(Qt6 COMPONENTS + Core + Gui + Qml + QuickControls2 + REQUIRED) +find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami CoreAddons I18n WindowSystem) +pkg_check_modules(libevdev REQUIRED IMPORTED_TARGET libevdev) + +add_executable(inputfaker main.cpp devicemanager.cpp) +ecm_add_qml_module(inputfaker + URI org.kde.inputfaker + VERSION 1.0) + +ecm_target_qml_sources(inputfaker SOURCES + ui/Main.qml) +target_link_libraries(inputfaker PRIVATE + Qt::Core + Qt::Qml + Qt::Quick + Qt::QuickControls2 + KF6::I18n + KF6::WindowSystem + KF6::CoreAddons + PkgConfig::libevdev +) + diff --git a/devicemanager.cpp b/devicemanager.cpp new file mode 100644 index 0000000..ab79676 --- /dev/null +++ b/devicemanager.cpp @@ -0,0 +1,135 @@ +#include "devicemanager.h" + +DeviceManager::~DeviceManager() { destroyDevice(); } + +QString DeviceManager::name() const +{ + return m_name; +} + +void DeviceManager::setName(const QString &name) +{ + if (m_name != name) + { + m_name = name; + Q_EMIT nameChanged(); + } +} + +bool DeviceManager::direct() const +{ + return m_direct; +} + +void DeviceManager::setDirect(bool direct) +{ + if (m_direct != direct) + { + m_direct = direct; + Q_EMIT directChanged(); + } +} + +bool DeviceManager::pad() const +{ + return m_pad; +} + +void DeviceManager::setPad(bool pad) +{ + if (m_pad != pad) + { + m_pad = pad; + Q_EMIT padChanged(); + } +} + +void DeviceManager::recreateDevice() +{ + destroyDevice(); + + input_absinfo xInfo{ + .value = 28166, + .minimum = 0, + .maximum = 47664, + .resolution = 100, + }; + + input_absinfo yInfo{ + .value = 22040, + .minimum = 0, + .maximum = 26778, + .resolution = 100, + }; + + input_absinfo pressureInfo{ + .minimum = 0, + .maximum = 8191, + }; + + input_absinfo tiltInfo{ + .minimum = -127, + .maximum = 127, + }; + + dev = libevdev_new(); + libevdev_set_name(dev, m_name.toUtf8().constData()); + libevdev_enable_event_type(dev, EV_SYN); + libevdev_enable_event_type(dev, EV_KEY); + if (!m_pad) + { + libevdev_enable_event_code(dev, EV_KEY, BTN_TOOL_PEN, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_TOOL_RUBBER, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_TOUCH, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_STYLUS, NULL); + + libevdev_enable_event_type(dev, EV_ABS); + libevdev_enable_event_code(dev, EV_ABS, ABS_X, &xInfo); + libevdev_enable_event_code(dev, EV_ABS, ABS_Y, &yInfo); + libevdev_enable_event_code(dev, EV_ABS, ABS_PRESSURE, &pressureInfo); + libevdev_enable_event_code(dev, EV_ABS, ABS_TILT_Y, &tiltInfo); + libevdev_enable_event_code(dev, EV_ABS, ABS_TILT_X, &tiltInfo); + + if (m_direct) + { + libevdev_enable_property(dev, INPUT_PROP_DIRECT); + } + } else + { + libevdev_enable_event_code(dev, EV_KEY, BTN_0, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_1, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_2, NULL); + libevdev_enable_event_code(dev, EV_KEY, BTN_3, NULL); + + libevdev_enable_event_type(dev, EV_REL); + libevdev_enable_event_code(dev, EV_REL, REL_HWHEEL, NULL); + libevdev_enable_event_code(dev, EV_REL, REL_WHEEL, NULL); + + libevdev_enable_event_type(dev, EV_MSC); + libevdev_enable_event_code(dev, EV_MSC, MSC_SCAN, NULL); + } + + rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev); + + uinput_fd = libevdev_uinput_get_fd(uidev); + + devnode = libevdev_uinput_get_devnode(uidev); + + fd = open(devnode, O_RDONLY); +} + +void DeviceManager::testButton() +{ + libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_PEN, 1); + libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_PEN, 0); +} + +void DeviceManager::destroyDevice() +{ + libevdev_free(dev); + libevdev_uinput_destroy(uidev); + close(fd); + + /* uinput fd is managed, so make sure it did get closed */ + close(uinput_fd); +} diff --git a/devicemanager.h b/devicemanager.h new file mode 100644 index 0000000..c65b58e --- /dev/null +++ b/devicemanager.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class DeviceManager : public QObject { + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QString name WRITE setName READ name NOTIFY nameChanged) + Q_PROPERTY(bool direct WRITE setDirect READ direct NOTIFY directChanged) + Q_PROPERTY(bool pad WRITE setPad READ pad NOTIFY padChanged) + +public: + ~DeviceManager() override; + + QString name() const; + void setName(const QString &name); + + bool direct() const; + void setDirect(bool direct); + + bool pad() const; + void setPad(bool pad); + +public Q_SLOTS: + void recreateDevice(); + void testButton(); + +Q_SIGNALS: + void nameChanged(); + void directChanged(); + void padChanged(); + +private: + void destroyDevice(); + + struct libevdev *dev = nullptr; + struct libevdev_uinput *uidev = nullptr; + int fd = 0, uinput_fd = 0; + unsigned int type = 0, code = 0; + int rc = 0; + const char *devnode = nullptr; + + QString m_name; + bool m_direct = false; + bool m_pad = false; +}; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6932c25 --- /dev/null +++ b/main.cpp @@ -0,0 +1,12 @@ +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication a(argc, argv); + + QQmlApplicationEngine engine; + engine.loadFromModule(QStringLiteral("org.kde.inputfaker"), QStringLiteral("Main")); + + return a.exec(); +} diff --git a/ui/Main.qml b/ui/Main.qml new file mode 100644 index 0000000..462c0b4 --- /dev/null +++ b/ui/Main.qml @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +import QtQuick +import QtQuick.Window +import org.kde.kirigami as Kirigami +import QtQuick.Controls as Controls +import QtQuick.Layouts +import org.kde.inputfaker +import org.kde.kirigamiaddons.formcard as FormCard + +Kirigami.ApplicationWindow { + id: appWindow + + width: 640 + height: 480 + visible: true + title: "InputFaker" + + property DeviceManager deviceManager: DeviceManager {} + + pageStack.initialPage: FormCard.FormCardPage { + id: page + + title: "FAKE!" + + FormCard.FormCard { + Layout.topMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + + FormCard.FormTextFieldDelegate { + label: "Device Name" + onTextChanged: deviceManager.name = text + } + + FormCard.FormDelegateSeparator {} + + FormCard.FormCheckDelegate { + text: "Direct" + onCheckedChanged: deviceManager.direct = checked + } + + FormCard.FormDelegateSeparator {} + + FormCard.FormCheckDelegate { + text: "Pad" + onCheckedChanged: deviceManager.pad = checked + } + + FormCard.FormDelegateSeparator {} + + FormCard.FormButtonDelegate { + text: "Apply & Re-create device" + onClicked: deviceManager.recreateDevice() + enabled: deviceManager.name.length > 0 + } + + FormCard.FormDelegateSeparator {} + + FormCard.FormButtonDelegate { + text: "Test button" + onClicked: deviceManager.testButton() + } + } + } +}