mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
hapticsplugin: Port to feedbackd
This is an initial port to feedbackd for the haptics plugin. This implementation is a simple port to have the motor enabled for a certain duration. We will eventually want to use feedbackd events to trigger these instead. Related MR for qtfeedback: https://invent.kde.org/jbbgameich/ktactilefeedback/-/merge_requests/2 https://invent.kde.org/teams/plasma-mobile/issues/-/issues/10
This commit is contained in:
parent
73b5595139
commit
72284989f8
8 changed files with 184 additions and 44 deletions
|
|
@ -45,7 +45,7 @@ Dependencies:
|
||||||
* Milou (for search)
|
* Milou (for search)
|
||||||
* Kirigami
|
* Kirigami
|
||||||
* Kirigami Addons
|
* Kirigami Addons
|
||||||
* hfd-service (optional: for vibrations)
|
* feedbackd (optional: for vibrations)
|
||||||
|
|
||||||
To start the shell in a window, run:
|
To start the shell in a window, run:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
# SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
qt_add_dbus_interfaces(DBUS_SRCS dbus/com.lomiri.hfd.xml)
|
set_source_files_properties(dbus/org.sigxcpu.Feedback.Haptic.xml PROPERTIES INCLUDE vibrationevent.h)
|
||||||
|
qt_add_dbus_interfaces(dbusinterface_SRCS
|
||||||
|
dbus/org.sigxcpu.Feedback.Haptic.xml)
|
||||||
|
|
||||||
ecm_add_qml_module(hapticsplugin URI org.kde.plasma.private.mobileshell.hapticsplugin GENERATE_PLUGIN_SOURCE)
|
ecm_add_qml_module(hapticsplugin URI org.kde.plasma.private.mobileshell.hapticsplugin GENERATE_PLUGIN_SOURCE)
|
||||||
target_sources(hapticsplugin PRIVATE
|
target_sources(hapticsplugin PRIVATE
|
||||||
|
vibrationevent.h
|
||||||
vibrationmanager.cpp
|
vibrationmanager.cpp
|
||||||
${DBUS_SRCS}
|
${dbusinterface_SRCS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hapticsplugin PRIVATE
|
target_link_libraries(hapticsplugin PRIVATE
|
||||||
|
|
@ -14,6 +17,7 @@ target_link_libraries(hapticsplugin PRIVATE
|
||||||
Qt::DBus
|
Qt::DBus
|
||||||
KF6::CoreAddons
|
KF6::CoreAddons
|
||||||
KF6::I18n
|
KF6::I18n
|
||||||
|
QCoro::DBus
|
||||||
)
|
)
|
||||||
|
|
||||||
ecm_finalize_qml_module(hapticsplugin)
|
ecm_finalize_qml_module(hapticsplugin)
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
|
||||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
||||||
<!--
|
|
||||||
SPDX-FileCopyrightText: 2020 Marius Gripsgard <marius@ubports.com>
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
-->
|
|
||||||
<node name="/com/lomiri/hfd">
|
|
||||||
<interface name="com.lomiri.hfd.Vibrator">
|
|
||||||
<method name="vibrate"/>
|
|
||||||
<method name="vibrate">
|
|
||||||
<arg name="durationMs" type="i" direction="in" />
|
|
||||||
</method>
|
|
||||||
<method name="rumble">
|
|
||||||
<arg name="durationMs" type="i" direction="in" />
|
|
||||||
<arg name="repeat" type="i" direction="in" />
|
|
||||||
</method>
|
|
||||||
</interface>
|
|
||||||
<interface name="com.lomiri.hfd.Leds">
|
|
||||||
<method name="setState">
|
|
||||||
<arg name="state" type="i" direction="in" />
|
|
||||||
</method>
|
|
||||||
<method name="setColor">
|
|
||||||
<arg name="color" type="u" direction="in" />
|
|
||||||
</method>
|
|
||||||
<method name="setOnMs">
|
|
||||||
<arg name="onMs" type="i" direction="in" />
|
|
||||||
</method>
|
|
||||||
<method name="setOffMs">
|
|
||||||
<arg name="offMs" type="i" direction="in" />
|
|
||||||
</method>
|
|
||||||
</interface>
|
|
||||||
</node>
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<!--
|
||||||
|
- SPDX-FileCopyrightText: 2025 Guido Günther <agx@sigxcpu.org>
|
||||||
|
- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
<node>
|
||||||
|
<!-- org.sigxcpu.Feedback.Haptic
|
||||||
|
@short_description: Interface to make a device vibrate
|
||||||
|
|
||||||
|
This D-Bus interface is used to make a device's haptic motor
|
||||||
|
vibrate. This is can be useful e.g. for games.
|
||||||
|
|
||||||
|
To provider user feedback the event based interface should be
|
||||||
|
preferred.
|
||||||
|
-->
|
||||||
|
<interface name="org.sigxcpu.Feedback.Haptic">
|
||||||
|
<!--
|
||||||
|
Vibrate:
|
||||||
|
@app_id: The application id usually in "reverse DNS" format
|
||||||
|
@pattern: The vibration pattern.
|
||||||
|
@success: Whether vibration was triggered
|
||||||
|
|
||||||
|
Triggers the given vibration pattern on the haptic device. The
|
||||||
|
pattern is a sequence of relative amplitude and duration pairs.
|
||||||
|
The amplitude must be between 0.0 and 1.0, durations are in
|
||||||
|
milliseconds.
|
||||||
|
-->
|
||||||
|
<method name="Vibrate">
|
||||||
|
<arg direction="in" name="app_id" type="s"/>
|
||||||
|
<arg direction="in" name="pattern" type="a(du)"/>
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="VibrationEventList" />
|
||||||
|
<arg direction="out" name="success" type="b"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</node>
|
||||||
80
components/hapticsplugin/dbus/org.sigxcpu.Feedback.xml
Normal file
80
components/hapticsplugin/dbus/org.sigxcpu.Feedback.xml
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<!--
|
||||||
|
- SPDX-FileCopyrightText: 2025 Guido Günther <agx@sigxcpu.org>
|
||||||
|
- SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
-->
|
||||||
|
<node>
|
||||||
|
<!-- org.sigxcpu.Feedback
|
||||||
|
@short_description: haptic/audio/visual feedback interface
|
||||||
|
|
||||||
|
This D-Bus interface is used to get the current feedback theme
|
||||||
|
and to give feedback on events.
|
||||||
|
-->
|
||||||
|
<interface name="org.sigxcpu.Feedback">
|
||||||
|
<!--
|
||||||
|
Profile: The currently used profile.
|
||||||
|
|
||||||
|
The currently used feedback profile name. Applications should
|
||||||
|
usually not change this value.
|
||||||
|
-->
|
||||||
|
<property name="Profile" type="s" access="readwrite" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TriggerFeedback:
|
||||||
|
@app_id: The application id usually in "reverse DNS" format
|
||||||
|
@event: The event name from the Event naming spec
|
||||||
|
@hints: Additional hints. Currently known hints
|
||||||
|
- profile: Override the profile used for this event with the given profile name
|
||||||
|
- important: Override the current global feedback level.
|
||||||
|
Together with the 'profile' hint this allows to trigger feedback for events
|
||||||
|
that would otherwise be disabled. A typical use case is an alarm clock. Note
|
||||||
|
that the feedback daemon (depending on it's configuration) might ignore this flag.
|
||||||
|
- sound-file: A custom sound file to play. This file will be used instead of any
|
||||||
|
sound event specified in the "full" profile. The sound will only be played if
|
||||||
|
appropriate for the feedback level of the event.
|
||||||
|
@timeout: When the feedbacks for this event should end latest in seconds. The special
|
||||||
|
values '-1' (just run each feedback once) and '0' (endless loop) are also supported.
|
||||||
|
@id: Event id for future reference
|
||||||
|
|
||||||
|
Give user feedback for an event by triggering feedbacks
|
||||||
|
defined in the daemon. The method call returns an event id
|
||||||
|
that can be used later on to e.g. cancel the triggered
|
||||||
|
feedbacks early.
|
||||||
|
|
||||||
|
Depending on the event, theme and profile several forms of
|
||||||
|
feedback will be triggered such as an audio ring tone and a
|
||||||
|
haptic motor.
|
||||||
|
-->
|
||||||
|
<method name="TriggerFeedback">
|
||||||
|
<arg direction="in" name="app_id" type="s"/>
|
||||||
|
<arg direction="in" name="event" type="s"/>
|
||||||
|
<arg direction="in" name="hints" type="a{sv}"/>
|
||||||
|
<annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap" />
|
||||||
|
<arg direction="in" name="timeout" type="i"/>
|
||||||
|
<arg direction="out" name="id" type="u"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
EndFeedback:
|
||||||
|
@id: The id of the event
|
||||||
|
|
||||||
|
End all feedbacks triggered by the event with the given id.
|
||||||
|
-->
|
||||||
|
<method name="EndFeedback">
|
||||||
|
<arg direction="in" name="id" type="u"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
FeedbackEnded:
|
||||||
|
@id: The id of the event
|
||||||
|
@reason: The reason why feedback was ended (currently unused).
|
||||||
|
|
||||||
|
Emitted when all feedbacks for an event have ended.
|
||||||
|
-->
|
||||||
|
<signal name="FeedbackEnded">
|
||||||
|
<arg name="id" type="u"/>
|
||||||
|
<arg name="reason" type="u"/>
|
||||||
|
</signal>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</node>
|
||||||
37
components/hapticsplugin/vibrationevent.h
Normal file
37
components/hapticsplugin/vibrationevent.h
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// SPDX-FileCopyrightText: 2025 Devin Lin <devin@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDBusArgument>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
|
class VibrationEvent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double amplitude;
|
||||||
|
quint32 duration;
|
||||||
|
};
|
||||||
|
|
||||||
|
using VibrationEventList = QList<VibrationEvent>;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(VibrationEvent)
|
||||||
|
Q_DECLARE_METATYPE(VibrationEventList)
|
||||||
|
|
||||||
|
inline QDBusArgument &operator<<(QDBusArgument &argument, const VibrationEvent &e)
|
||||||
|
{
|
||||||
|
argument.beginStructure();
|
||||||
|
argument << e.amplitude;
|
||||||
|
argument << e.duration;
|
||||||
|
argument.endStructure();
|
||||||
|
return argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QDBusArgument &operator>>(const QDBusArgument &argument, VibrationEvent &e)
|
||||||
|
{
|
||||||
|
argument.beginStructure();
|
||||||
|
argument >> e.amplitude;
|
||||||
|
argument >> e.duration;
|
||||||
|
argument.endStructure();
|
||||||
|
return argument;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
|
// SPDX-FileCopyrightText: 2023-2025 Devin Lin <devin@kde.org>
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "vibrationmanager.h"
|
#include "vibrationmanager.h"
|
||||||
|
|
@ -6,14 +6,24 @@
|
||||||
VibrationManager::VibrationManager(QObject *parent)
|
VibrationManager::VibrationManager(QObject *parent)
|
||||||
: QObject{parent}
|
: QObject{parent}
|
||||||
{
|
{
|
||||||
|
qDBusRegisterMetaType<VibrationEvent>();
|
||||||
|
qDBusRegisterMetaType<VibrationEventList>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VibrationManager::vibrate(int durationMs)
|
QCoro::Task<void> VibrationManager::vibrate(int durationMs)
|
||||||
{
|
{
|
||||||
// Only create interface when needed.
|
// Only create interface when needed.
|
||||||
if (!m_interface) {
|
if (!m_interface) {
|
||||||
const auto objectPath = QStringLiteral("/com/lomiri/hfd");
|
const auto objectPath = QStringLiteral("/org/sigxcpu/Feedback");
|
||||||
m_interface = new com::lomiri::hfd::Vibrator("com.lomiri.hfd", objectPath, QDBusConnection::systemBus(), this);
|
m_interface = new OrgSigxcpuFeedbackHapticInterface("org.sigxcpu.Feedback", objectPath, QDBusConnection::sessionBus(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString appId = QStringLiteral("org.kde.plasmashell");
|
||||||
|
const VibrationEvent event{1.0, static_cast<quint32>(durationMs)};
|
||||||
|
const VibrationEventList pattern = {event};
|
||||||
|
QDBusPendingReply<bool> reply = co_await m_interface->Vibrate(appId, pattern);
|
||||||
|
|
||||||
|
if (!reply.isValid() || !reply.value()) {
|
||||||
|
qWarning() << "feedbackd vibration failed";
|
||||||
}
|
}
|
||||||
m_interface->vibrate(durationMs);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <qqmlregistration.h>
|
#include <qqmlregistration.h>
|
||||||
|
|
||||||
#include "hfdinterface.h"
|
#include "hapticinterface.h"
|
||||||
|
#include "vibrationevent.h"
|
||||||
|
|
||||||
|
#include <QCoroDBusPendingReply>
|
||||||
|
|
||||||
class VibrationManager : public QObject
|
class VibrationManager : public QObject
|
||||||
{
|
{
|
||||||
|
|
@ -17,8 +21,8 @@ class VibrationManager : public QObject
|
||||||
public:
|
public:
|
||||||
VibrationManager(QObject *parent = nullptr);
|
VibrationManager(QObject *parent = nullptr);
|
||||||
|
|
||||||
Q_INVOKABLE void vibrate(int durationMs);
|
Q_INVOKABLE QCoro::Task<void> vibrate(int durationMs);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
com::lomiri::hfd::Vibrator *m_interface{nullptr};
|
OrgSigxcpuFeedbackHapticInterface *m_interface{nullptr};
|
||||||
};
|
};
|
||||||
Loading…
Reference in a new issue