/* SPDX-FileCopyrightText: 2018 Roman Gilg SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "InputDevice_interface.h" #include #include #include #include #include "kwindevices-logging.h" class InputDeviceTest; namespace KWinDevices { class InputDevice : public QObject { Q_OBJECT Q_PROPERTY(QString sysName READ sysName CONSTANT) Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QSizeF size READ size CONSTANT) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT) Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged) Q_PROPERTY(bool supportsCalibrationMatrix READ supportsCalibrationMatrix CONSTANT) Q_PROPERTY(bool calibrationMatrixIsDefault READ calibrationMatrixIsDefault NOTIFY calibrationMatrixChanged) Q_PROPERTY(bool supportsOrientation READ supportsOrientation CONSTANT) Q_PROPERTY(int orientation READ orientation WRITE setOrientation NOTIFY orientationChanged) Q_PROPERTY(QString outputName READ outputName WRITE setOutputName NOTIFY outputNameChanged) Q_PROPERTY(QRectF outputArea READ outputArea WRITE setOutputArea NOTIFY outputAreaChanged) Q_PROPERTY(QRectF inputArea READ inputArea WRITE setInputArea NOTIFY inputAreaChanged) Q_PROPERTY(bool supportsInputArea READ supportsInputArea CONSTANT) Q_PROPERTY(bool mapToWorkspace READ isMapToWorkspace WRITE setMapToWorkspace NOTIFY mapToWorkspaceChanged) Q_PROPERTY(QString pressureCurve READ pressureCurve WRITE setPressureCurve NOTIFY pressureCurveChanged) Q_PROPERTY(bool pressureCurveIsDefault READ pressureCurveIsDefault NOTIFY pressureCurveChanged) Q_PROPERTY(quint32 tabletPadButtonCount READ tabletPadButtonCount CONSTANT) Q_PROPERTY(double pressureRangeMin READ pressureRangeMin WRITE setPressureRangeMin NOTIFY pressureRangeMinChanged) Q_PROPERTY(double pressureRangeMax READ pressureRangeMax WRITE setPressureRangeMax NOTIFY pressureRangeMaxChanged) Q_PROPERTY(bool supportsPressureRange READ supportsPressureRange CONSTANT) Q_PROPERTY(bool relative READ isRelative WRITE setRelative NOTIFY relativeChanged) Q_PROPERTY(quint32 tabletPadDialCount READ tabletPadDialCount CONSTANT) Q_PROPERTY(quint32 tabletPadRingCount READ tabletPadRingCount CONSTANT) Q_PROPERTY(QList numModes READ numModes CONSTANT) Q_PROPERTY(QList currentModes READ currentModes NOTIFY currentModesChanged) Q_PROPERTY(bool isVirtual READ isVirtual CONSTANT) public: InputDevice(const QString &dbusName, QObject *parent); void load(); void save(); void defaults(); bool isSaveNeeded() const; bool isDefaults() const; QString name() const { return m_name.value(); } QString sysName() const { return m_sysName.value(); } bool isLeftHanded() const { return m_leftHanded.value(); } bool supportsLeftHanded() const { return m_leftHanded.isSupported(); } void setLeftHanded(bool set); bool supportsOrientation() const { return m_orientation.isSupported(); } int orientation() const { return m_orientation.value(); } void setOrientation(int ori); QString outputName() const { return m_outputName.value(); } void setOutputName(const QString &outputName); QSizeF size() const { return m_size.value(); } QRectF outputArea() const { return m_outputArea.value(); } void setOutputArea(const QRectF &outputArea); QRectF inputArea() const { return m_inputArea.isSupported() ? m_inputArea.value() : QRectF(0, 0, 1, 1); } void setInputArea(const QRectF &inputArea); bool supportsInputArea() const { return m_inputArea.isSupported(); } bool supportsCalibrationMatrix() const { return m_calibrationMatrix.isSupported(); } bool isEnabled() const { return m_enabled.value(); } QMatrix4x4 defaultCalibrationMatrix() const { return deserializeMatrix(m_calibrationMatrix.defaultValue()); } QMatrix4x4 calibrationMatrix() const { return deserializeMatrix(m_calibrationMatrix.value()); } void setCalibrationMatrix(const QMatrix4x4 &matrix) { m_calibrationMatrix.set(serializeMatrix(matrix)); m_calibrationMatrix.save(); } Q_INVOKABLE void resetCalibrationMatrix() { m_calibrationMatrix.resetFromDefaults(); m_calibrationMatrix.save(); } bool calibrationMatrixIsDefault() const { return m_calibrationMatrix.isDefaults(); } void setEnabled(bool enabled); bool isMapToWorkspace() const { return m_mapToWorkspace.value(); } void setMapToWorkspace(bool mapToWorkspace); QString pressureCurve() const { return m_pressureCurve.value(); } void setPressureCurve(const QString &curve); bool pressureCurveIsDefault() const; void setPressureRangeMin(double min) { m_pressureRangeMin.set(min); } double pressureRangeMin() const { return m_pressureRangeMin.value(); } void setPressureRangeMax(double max) { m_pressureRangeMax.set(max); } double pressureRangeMax() const { return m_pressureRangeMax.value(); } bool supportsPressureRange() const { return m_pressureRangeMin.isSupported() && m_pressureRangeMax.isSupported(); } quint32 tabletPadButtonCount() const { return m_tabletPadButtonCount.value(); } quint32 tabletPadDialCount() const { return m_tabletPadDialCount.value(); } quint32 tabletPadRingCount() const { return m_tabletPadRingCount.value(); } QList numModes() const { return m_numModes.value(); } QList currentModes() const { return m_currentModes.value(); } QString deviceGroup() const { return m_deviceGroup.value(); } bool tabletPad() const { return m_tabletPad.value(); } bool tabletTool() const { return m_tabletTool.value(); } bool isRelative() const { return m_relative.value(); } void setRelative(bool relative); bool isVirtual() const { return m_isVirtual.value(); } Q_SIGNALS: void needsSaveChanged(); void leftHandedChanged(); void orientationChanged(); void outputNameChanged(); void outputAreaChanged(); void enabledChanged(); void mapToWorkspaceChanged(); void pressureCurveChanged(); void inputAreaChanged(); void pressureRangeMinChanged(); void pressureRangeMaxChanged(); void calibrationMatrixChanged(); void relativeChanged(); void currentModesChanged(); protected: static QString serializeMatrix(const QMatrix4x4 &matrix); static QMatrix4x4 deserializeMatrix(const QString &matrix); friend class ::InputDeviceTest; private: template struct Prop { typedef T (OrgKdeKWinInputDeviceInterface::*ValueFunction)() const; typedef bool (OrgKdeKWinInputDeviceInterface::*SupportedFunction)() const; typedef bool (OrgKdeKWinInputDeviceInterface::*SetterFunction)(const T &value); typedef void (InputDevice::*ChangedSignal)(); explicit Prop(InputDevice *device, const char *propName, ValueFunction defaultValueFunction, SupportedFunction supported, ChangedSignal changedSignal) : m_defaultValueFunction(defaultValueFunction) , m_supportedFunction(supported) , m_changedSignalFunction(changedSignal) , m_device(device) { int idx = OrgKdeKWinInputDeviceInterface::staticMetaObject.indexOfProperty(propName); if (idx < 0) { qCDebug(LIBKWINDEVICES) << "there is no" << propName; } Q_ASSERT(idx >= 0); m_prop = OrgKdeKWinInputDeviceInterface::staticMetaObject.property(idx); } explicit Prop(InputDevice *device, const char *propName) : m_defaultValueFunction(nullptr) , m_supportedFunction(nullptr) , m_changedSignalFunction(nullptr) , m_device(device) { Q_ASSERT(m_device); int idx = OrgKdeKWinInputDeviceInterface::staticMetaObject.indexOfProperty(propName); Q_ASSERT(idx >= 0); m_prop = OrgKdeKWinInputDeviceInterface::staticMetaObject.property(idx); } T value() const { if (!m_value.has_value()) { auto iface = m_device->m_iface.get(); if (isSupported()) { m_value = m_prop.read(iface).value(); } } return m_value ? m_value.value() : T(); } void resetFromDefaults() { if (isSupported()) { set(defaultValue()); } } void resetFromSaved() { m_value = {}; value(); m_configValue = m_value; if (m_changedSignalFunction) { (m_device->*m_changedSignalFunction)(); } } void set(T newVal) { if (!m_value) { value(); } Q_ASSERT(isSupported()); if (m_value != newVal) { m_value = newVal; if (m_changedSignalFunction) { (m_device->*m_changedSignalFunction)(); } } } T defaultValue() const { return m_defaultValueFunction ? (m_device->m_iface.get()->*m_defaultValueFunction)() : T(); } bool changed() const { return m_value.has_value() && m_value.value() != m_configValue; } void set(const Prop &p) { set(p.value()); } bool isSupported() const { auto iface = m_device->m_iface.get(); return !m_supportedFunction || (iface->*m_supportedFunction)(); } bool save() { if (!isSupported() || !m_value || m_prop.isConstant()) { qCDebug(LIBKWINDEVICES) << "skipping" << this << m_value.has_value() << isSupported() << m_prop.name(); return false; } auto iface = m_device->m_iface.get(); const bool ret = m_prop.write(iface, *m_value); if (ret) { m_configValue = *m_value; } return ret; } bool isDefaults() const { return m_value == defaultValue(); } private: QMetaProperty m_prop; const ValueFunction m_defaultValueFunction; const SupportedFunction m_supportedFunction; const ChangedSignal m_changedSignalFunction; InputDevice *const m_device; mutable std::optional m_configValue; mutable std::optional m_value; }; // // general Prop m_name = Prop(this, "name"); Prop m_size = Prop(this, "size"); Prop m_sysName = Prop(this, "sysName"); Prop m_leftHanded = Prop(this, "leftHanded", &OrgKdeKWinInputDeviceInterface::leftHandedEnabledByDefault, &OrgKdeKWinInputDeviceInterface::supportsLeftHanded, &InputDevice::leftHandedChanged); Prop m_orientation = Prop(this, "orientationDBus", nullptr, &OrgKdeKWinInputDeviceInterface::supportsCalibrationMatrix, &InputDevice::orientationChanged); Prop m_enabled = Prop(this, "enabled", &OrgKdeKWinInputDeviceInterface::enabledByDefault, nullptr, &InputDevice::enabledChanged); Prop m_outputName = Prop(this, "outputName", nullptr, nullptr, &InputDevice::outputNameChanged); Prop m_outputArea = Prop(this, "outputArea", &OrgKdeKWinInputDeviceInterface::defaultOutputArea, &OrgKdeKWinInputDeviceInterface::supportsOutputArea, &InputDevice::outputAreaChanged); Prop m_inputArea = Prop(this, "inputArea", &OrgKdeKWinInputDeviceInterface::defaultInputArea, &OrgKdeKWinInputDeviceInterface::supportsInputArea, &InputDevice::inputAreaChanged); Prop m_relative = Prop(this, "tabletToolIsRelative", nullptr, nullptr, &InputDevice::relativeChanged); Prop m_mapToWorkspace = Prop(this, "mapToWorkspace", &OrgKdeKWinInputDeviceInterface::defaultMapToWorkspace, nullptr, &InputDevice::mapToWorkspaceChanged); Prop m_calibrationMatrix = Prop(this, "calibrationMatrix", &OrgKdeKWinInputDeviceInterface::defaultCalibrationMatrix, &OrgKdeKWinInputDeviceInterface::supportsCalibrationMatrix, &InputDevice::calibrationMatrixChanged); Prop m_pressureCurve = Prop(this, "pressureCurve", &OrgKdeKWinInputDeviceInterface::defaultPressureCurve, nullptr, &InputDevice::pressureCurveChanged); Prop m_tabletPadButtonCount = Prop(this, "tabletPadButtonCount"); Prop m_tabletPadDialCount = Prop(this, "tabletPadDialCount"); Prop m_tabletPadRingCount = Prop(this, "tabletPadRingCount"); Prop> m_numModes = Prop>(this, "numModes"); Prop> m_currentModes = Prop>(this, "currentModes", nullptr, nullptr, &InputDevice::currentModesChanged); Prop m_pressureRangeMin = Prop(this, "pressureRangeMin", &OrgKdeKWinInputDeviceInterface::defaultPressureRangeMin, &OrgKdeKWinInputDeviceInterface::supportsPressureRange, &InputDevice::pressureRangeMinChanged); Prop m_pressureRangeMax = Prop(this, "pressureRangeMax", &OrgKdeKWinInputDeviceInterface::defaultPressureRangeMax, &OrgKdeKWinInputDeviceInterface::supportsPressureRange, &InputDevice::pressureRangeMaxChanged); Prop m_deviceGroup = Prop(this, "deviceGroupId"); Prop m_tabletPad = Prop(this, "tabletPad"); Prop m_tabletTool = Prop(this, "tabletTool"); Prop m_isVirtual = Prop(this, "isVirtual"); std::unique_ptr m_iface; }; } // namespace KWinDevices