From b724789ab194ba9d93bb8a241d19feef3d1db29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:44 +0100 Subject: [PATCH 01/14] dev-vibra: Untabify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gbp-Dch: Ignore Signed-off-by: Guido Günther --- src/fbd-dev-vibra.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fbd-dev-vibra.c b/src/fbd-dev-vibra.c index 1f78136..fc72f27 100644 --- a/src/fbd-dev-vibra.c +++ b/src/fbd-dev-vibra.c @@ -272,7 +272,7 @@ fbd_dev_vibra_rumble (FbdDevVibra *self, guint duration, gboolean upload) /* TODO: fall back to multiple rumbles when sine not supported */ gboolean fbd_dev_vibra_periodic (FbdDevVibra *self, guint duration, guint magnitude, - guint fade_in_level, guint fade_in_time) + guint fade_in_level, guint fade_in_time) { struct input_event event; struct ff_effect effect = { 0 }; @@ -340,7 +340,7 @@ fbd_dev_vibra_remove_effect (FbdDevVibra *self) gboolean -fbd_dev_vibra_stop(FbdDevVibra *self) +fbd_dev_vibra_stop (FbdDevVibra *self) { struct input_event stop = { 0 }; @@ -359,7 +359,7 @@ fbd_dev_vibra_stop(FbdDevVibra *self) } GUdevDevice * -fbd_dev_vibra_get_device(FbdDevVibra *self) +fbd_dev_vibra_get_device (FbdDevVibra *self) { g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE); From 5238d469688125e0f2db33337b04a02cd93e8170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:45 +0100 Subject: [PATCH 02/14] feedback-rumble: Fix indent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There were some tabs from day 0 Signed-off-by: Guido Günther --- src/fbd-feedback-vibra-rumble.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fbd-feedback-vibra-rumble.c b/src/fbd-feedback-vibra-rumble.c index b4ec083..5aad9f5 100644 --- a/src/fbd-feedback-vibra-rumble.c +++ b/src/fbd-feedback-vibra-rumble.c @@ -43,9 +43,9 @@ G_DEFINE_TYPE (FbdFeedbackVibraRumble, fbd_feedback_vibra_rumble, FBD_TYPE_FEEDB static void fbd_feedback_vibra_rumble_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) + guint property_id, + const GValue *value, + GParamSpec *pspec) { FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (object); @@ -63,10 +63,10 @@ fbd_feedback_vibra_rumble_set_property (GObject *object, } static void -fbd_feedback_vibra_rumble_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) +fbd_feedback_vibra_rumble_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) { FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (object); @@ -129,7 +129,7 @@ fbd_feedback_vibra_rumble_start_vibra (FbdFeedbackVibra *vibra) self->periods = self->count; g_debug ("Rumble Vibra event: duration %d, rumble: %d, pause: %d, period: %d", - duration, self->rumble, self->pause, period); + duration, self->rumble, self->pause, period); fbd_dev_vibra_rumble (dev, self->rumble, TRUE); self->periods--; if (self->periods) { From 8a11565cc1da2868a0628b557f7b8edec8e478e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:45 +0100 Subject: [PATCH 03/14] dev-vibra: Allow to check if the device is busy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We accept `NULL` to make the case of no haptic easy to handle. Signed-off-by: Guido Günther --- src/fbd-dev-vibra.c | 28 ++++++++++++++++++++++++++++ src/fbd-dev-vibra.h | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/fbd-dev-vibra.c b/src/fbd-dev-vibra.c index fc72f27..2e7584e 100644 --- a/src/fbd-dev-vibra.c +++ b/src/fbd-dev-vibra.c @@ -219,6 +219,7 @@ fbd_dev_vibra_class_init (FbdDevVibraClass *klass) static void fbd_dev_vibra_init (FbdDevVibra *self) { + self->id = -1; } FbdDevVibra * @@ -330,11 +331,16 @@ fbd_dev_vibra_remove_effect (FbdDevVibra *self) { g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE); + if (self->id == -1) + return TRUE; + g_debug("Erasing vibra effect (%d)", self->fd); if (ioctl(self->fd, EVIOCRMFF, self->id) == -1) { g_warning ("Failed to erase vibra effect with id %d: %s", self->id, strerror(errno)); + self->id = -1; return FALSE; } + self->id = -1; return TRUE; } @@ -346,6 +352,9 @@ fbd_dev_vibra_stop (FbdDevVibra *self) g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE); + if (self->id == -1) + return TRUE; + stop.type = EV_FF; stop.code = self->id; stop.value = 0; @@ -365,3 +374,22 @@ fbd_dev_vibra_get_device (FbdDevVibra *self) return self->device; } + +/** + * fbd_dev_vibra_is_busy: + * @self: The vibra device + * + * Check whether the device is currently in used + * + * Returns: `TRUE` when the device is in use, otherwise `FALSE` + */ +gboolean +fbd_dev_vibra_is_busy (FbdDevVibra *self) +{ + if (self == NULL) + return FALSE; + + g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), TRUE); + + return self->id != -1; +} diff --git a/src/fbd-dev-vibra.h b/src/fbd-dev-vibra.h index 55c03c6..a653d63 100644 --- a/src/fbd-dev-vibra.h +++ b/src/fbd-dev-vibra.h @@ -21,6 +21,6 @@ gboolean fbd_dev_vibra_periodic (FbdDevVibra *self, guint duration, guint ma gboolean fbd_dev_vibra_stop (FbdDevVibra *self); gboolean fbd_dev_vibra_remove_effect (FbdDevVibra *self); GUdevDevice *fbd_dev_vibra_get_device(FbdDevVibra *self); - +gboolean fbd_dev_vibra_is_busy (FbdDevVibra *self); G_END_DECLS From cdf5fb2b2d66c171a97a6d5bfca6340ca74926f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:46 +0100 Subject: [PATCH 04/14] feedback-manager: Split out feedback level calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to use it from other places too and while at that documents it. Signed-off-by: Guido Günther --- src/fbd-feedback-manager.c | 47 ++++++++++++++++++++++++++++++-------- src/fbd-feedback-manager.h | 5 ++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/fbd-feedback-manager.c b/src/fbd-feedback-manager.c index 8a647de..19dbd06 100644 --- a/src/fbd-feedback-manager.c +++ b/src/fbd-feedback-manager.c @@ -351,6 +351,7 @@ parse_hints (GVariant *hints, FbdFeedbackProfileLevel *level, gboolean *hint_imp return TRUE; } + static gboolean fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, GDBusMethodInvocation *invocation, @@ -364,9 +365,9 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, GSList *feedbacks, *l; guint event_id; const gchar *sender; - FbdFeedbackProfileLevel app_level, level, hint_level = FBD_FEEDBACK_PROFILE_LEVEL_FULL; + FbdFeedbackProfileLevel level, hint_level = FBD_FEEDBACK_PROFILE_LEVEL_FULL; gboolean found_fb = FALSE; - gboolean hint_important = FALSE, can_important; + gboolean hint_important = FALSE; sender = g_dbus_method_invocation_get_sender (invocation); g_debug ("Event '%s' for '%s' from %s", arg_event, arg_app_id, sender); @@ -405,15 +406,9 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, event = fbd_event_new (event_id, arg_app_id, arg_event, arg_timeout, sender); g_hash_table_insert (self->events, GUINT_TO_POINTER (event_id), event); - app_level = app_get_feedback_level (arg_app_id); - can_important = app_is_important (self, arg_app_id); - - if (hint_important && can_important) - level = hint_level; - else - level = get_max_level (self->level, app_level, hint_level); - + level = fbd_feedback_manager_get_effective_level (self, arg_app_id, hint_level, hint_important); feedbacks = fbd_feedback_theme_lookup_feedback (self->theme, level, event); + if (feedbacks) { for (l = feedbacks; l; l = l->next) { FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); @@ -662,3 +657,35 @@ fbd_feedback_manager_set_profile (FbdFeedbackManager *self, const gchar *profile return TRUE; } + +/** + * fbd_feedback_manager_get_effective_level: + * @self: The feedback manager + * @app_id: The app-id of the app that triggered the feedback + * @want_level: The wanted level + * @important: Whether the important hint is set + * + * Calculates the effective feedback level taking the hints sent for the event + * and the system configuration into account + * + * Returns: The effective feedback level + */ +FbdFeedbackProfileLevel +fbd_feedback_manager_get_effective_level (FbdFeedbackManager *self, + const char *app_id, + FbdFeedbackProfileLevel want_level, + gboolean important) +{ + gboolean can_important; + FbdFeedbackProfileLevel app_level, level; + + app_level = app_get_feedback_level (app_id); + can_important = app_is_important (self, app_id); + + if (important && can_important) + level = want_level; + else + level = get_max_level (self->level, app_level, want_level); + + return level; +} diff --git a/src/fbd-feedback-manager.h b/src/fbd-feedback-manager.h index 76291d9..7614333 100644 --- a/src/fbd-feedback-manager.h +++ b/src/fbd-feedback-manager.h @@ -8,6 +8,7 @@ #include "fbd-dev-vibra.h" #include "fbd-dev-leds.h" #include "fbd-dev-sound.h" +#include "fbd-feedback-profile.h" #include "lfb-gdbus.h" #include @@ -24,5 +25,9 @@ FbdDevSound *fbd_feedback_manager_get_dev_sound (FbdFeedbackManager *self); FbdDevLeds *fbd_feedback_manager_get_dev_leds (FbdFeedbackManager *self); void fbd_feedback_manager_load_theme (FbdFeedbackManager *self); gboolean fbd_feedback_manager_set_profile (FbdFeedbackManager *self, const gchar *profile); +FbdFeedbackProfileLevel fbd_feedback_manager_get_effective_level (FbdFeedbackManager *self, + const char *app_id, + FbdFeedbackProfileLevel want_level, + gboolean important); G_END_DECLS From 5ed445a81d966b7929e61dfc366e82184b2fb7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:46 +0100 Subject: [PATCH 05/14] feedback-manager: Don't trigger haptic feedbacks when vibra is busy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This just gives odd vibration. Later on we should use priorities to allow for higher priority events like phone calls to override lower priority events like e.g. button presses. This is similar to how we want to handle LED patterns. Signed-off-by: Guido Günther --- src/fbd-feedback-manager.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fbd-feedback-manager.c b/src/fbd-feedback-manager.c index 19dbd06..7bfac52 100644 --- a/src/fbd-feedback-manager.c +++ b/src/fbd-feedback-manager.c @@ -414,6 +414,10 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); if (fbd_feedback_is_available (FBD_FEEDBACK_BASE (fb))) { + /* Handle one haptic feedback at a time */ + /* TODO: should use priorities */ + if (FBD_IS_FEEDBACK_VIBRA (fb) && fbd_dev_vibra_is_busy (self->vibra)) + continue; fbd_event_add_feedback (event, fb); found_fb = TRUE; } From 24736b949017f91c0ecd9b8031cbbb6b5aa46ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:47 +0100 Subject: [PATCH 06/14] feedback-vibra: Use g_timeout_add_once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix some indent while at that Signed-off-by: Guido Günther --- src/fbd-feedback-vibra.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/fbd-feedback-vibra.c b/src/fbd-feedback-vibra.c index 9c4555c..30a3e1d 100644 --- a/src/fbd-feedback-vibra.c +++ b/src/fbd-feedback-vibra.c @@ -35,17 +35,17 @@ typedef struct _FbdFeedbackVibraPrivate { G_DEFINE_TYPE_WITH_PRIVATE (FbdFeedbackVibra, fbd_feedback_vibra, FBD_TYPE_FEEDBACK_BASE); -static gboolean +static void on_timeout_expired (FbdFeedbackVibra *self) { FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); - fbd_dev_vibra_remove_effect (dev); - fbd_feedback_base_done (FBD_FEEDBACK_BASE(self)); - return G_SOURCE_REMOVE; + fbd_dev_vibra_stop (dev); + fbd_feedback_base_done (FBD_FEEDBACK_BASE (self)); } + static void fbd_feedback_vibra_run (FbdFeedbackBase *base) { @@ -57,9 +57,9 @@ fbd_feedback_vibra_run (FbdFeedbackBase *base) g_return_if_fail (klass->start_vibra); klass->start_vibra (self); - priv->timer_id = g_timeout_add (priv->duration, - (GSourceFunc)on_timeout_expired, - self); + priv->timer_id = g_timeout_add_once (priv->duration, + (GSourceOnceFunc)on_timeout_expired, + self); g_source_set_name_by_id (priv->timer_id, "feedback-vibra-timer"); } @@ -75,8 +75,8 @@ fbd_feedback_vibra_end (FbdFeedbackBase *base) return; g_return_if_fail (klass->end_vibra); - klass->end_vibra(self); - g_clear_handle_id(&priv->timer_id, g_source_remove); + klass->end_vibra (self); + g_clear_handle_id (&priv->timer_id, g_source_remove); fbd_feedback_base_done (FBD_FEEDBACK_BASE(self)); } @@ -102,9 +102,9 @@ fbd_feedback_vibra_set_property (GObject *object, static void fbd_feedback_vibra_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) + guint property_id, + GValue *value, + GParamSpec *pspec) { FbdFeedbackVibra *self = FBD_FEEDBACK_VIBRA (object); FbdFeedbackVibraPrivate *priv = fbd_feedback_vibra_get_instance_private (self); From 18f2c88746c779d6751e75bb3bf7c185836d42b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:48 +0100 Subject: [PATCH 07/14] feedback-vibra: Clear timer id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we clear the timer id when vibra ended. Signed-off-by: Guido Günther --- src/fbd-feedback-vibra.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fbd-feedback-vibra.c b/src/fbd-feedback-vibra.c index 30a3e1d..e771e49 100644 --- a/src/fbd-feedback-vibra.c +++ b/src/fbd-feedback-vibra.c @@ -39,9 +39,11 @@ static void on_timeout_expired (FbdFeedbackVibra *self) { FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdFeedbackVibraPrivate *priv = fbd_feedback_vibra_get_instance_private (self); FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); fbd_dev_vibra_stop (dev); + priv->timer_id = 0; fbd_feedback_base_done (FBD_FEEDBACK_BASE (self)); } From 58221c741605fad68948c73bb084402312e6fe95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:48 +0100 Subject: [PATCH 08/14] feedback-vibra: Allow derived classes to override duration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows the derived classes to perform the length duration calculation. The parent class can thus still keep track of the total feedback length. Signed-off-by: Guido Günther --- src/fbd-feedback-vibra-priv.h | 14 ++++++++++++++ src/fbd-feedback-vibra.c | 29 +++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/fbd-feedback-vibra-priv.h diff --git a/src/fbd-feedback-vibra-priv.h b/src/fbd-feedback-vibra-priv.h new file mode 100644 index 0000000..445bf3d --- /dev/null +++ b/src/fbd-feedback-vibra-priv.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0+ + */ +#pragma once + +#include + +G_BEGIN_DECLS + +void fbd_feedback_vibra_set_duration (FbdFeedbackVibra *self, guint duration); + +G_END_DECLS diff --git a/src/fbd-feedback-vibra.c b/src/fbd-feedback-vibra.c index e771e49..a04a902 100644 --- a/src/fbd-feedback-vibra.c +++ b/src/fbd-feedback-vibra.c @@ -8,6 +8,7 @@ #include "fbd-enums.h" #include "fbd-feedback-vibra.h" +#include "fbd-feedback-vibra-priv.h" #include "fbd-feedback-manager.h" /** @@ -133,13 +134,16 @@ fbd_feedback_vibra_class_init (FbdFeedbackVibraClass *klass) base_class->run = fbd_feedback_vibra_run; base_class->end = fbd_feedback_vibra_end; + /** + * FbdFeedbackVibra:duration: + * + * The total duration of the feedback event in milliseconds. + */ props[PROP_DURATION] = g_param_spec_uint ( - "duration", - "Duration", - "Vibra event duration in msecs", + "duration", "", "", 0, G_MAXUINT, FBD_FEEDBACK_VIBRA_DEFAULT_DURATION, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST_PROP, props); } @@ -158,3 +162,20 @@ fbd_feedback_vibra_get_duration (FbdFeedbackVibra *self) priv = fbd_feedback_vibra_get_instance_private (self); return priv->duration; } + + +void +fbd_feedback_vibra_set_duration (FbdFeedbackVibra *self, guint duration) +{ + FbdFeedbackVibraPrivate *priv; + + g_return_if_fail (FBD_IS_FEEDBACK_VIBRA (self)); + + priv = fbd_feedback_vibra_get_instance_private (self); + + if (duration == priv->duration) + return; + + priv->duration = duration; + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_DURATION]); +} From 386c98c3819ddce2bfcbe81907e9b1739907dcbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 17:49:45 +0100 Subject: [PATCH 09/14] dev-vibra: Allow to set magnitude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows for more subtle feedback Signed-off-by: Guido Günther --- src/fbd-dev-vibra.c | 4 ++-- src/fbd-dev-vibra.h | 5 ++++- src/fbd-feedback-vibra-rumble.c | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/fbd-dev-vibra.c b/src/fbd-dev-vibra.c index 2e7584e..c99444f 100644 --- a/src/fbd-dev-vibra.c +++ b/src/fbd-dev-vibra.c @@ -233,7 +233,7 @@ fbd_dev_vibra_new (GUdevDevice *device, GError **error) } gboolean -fbd_dev_vibra_rumble (FbdDevVibra *self, guint duration, gboolean upload) +fbd_dev_vibra_rumble (FbdDevVibra *self, double magnitude, guint duration, gboolean upload) { struct input_event event = { 0 }; struct ff_effect effect = { 0 }; @@ -243,7 +243,7 @@ fbd_dev_vibra_rumble (FbdDevVibra *self, guint duration, gboolean upload) memset(&effect, 0, sizeof(effect)); effect.type = FF_RUMBLE; effect.id = -1; - effect.u.rumble.strong_magnitude = 0x8000; + effect.u.rumble.strong_magnitude = (0x8000 * magnitude); effect.u.rumble.weak_magnitude = 0; effect.replay.length = duration; effect.replay.delay = 0; diff --git a/src/fbd-dev-vibra.h b/src/fbd-dev-vibra.h index a653d63..ef5891b 100644 --- a/src/fbd-dev-vibra.h +++ b/src/fbd-dev-vibra.h @@ -15,7 +15,10 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (FbdDevVibra, fbd_dev_vibra, FBD, DEV_VIBRA, GObject); FbdDevVibra *fbd_dev_vibra_new (GUdevDevice *device, GError **error); -gboolean fbd_dev_vibra_rumble (FbdDevVibra *device, guint duration, gboolean upload); +gboolean fbd_dev_vibra_rumble (FbdDevVibra *device, + double magnitude, + guint duration, + gboolean upload); gboolean fbd_dev_vibra_periodic (FbdDevVibra *self, guint duration, guint magnitude, guint fade_in_level, guint fade_in_time); gboolean fbd_dev_vibra_stop (FbdDevVibra *self); diff --git a/src/fbd-feedback-vibra-rumble.c b/src/fbd-feedback-vibra-rumble.c index 5aad9f5..534c9a1 100644 --- a/src/fbd-feedback-vibra-rumble.c +++ b/src/fbd-feedback-vibra-rumble.c @@ -92,7 +92,7 @@ on_period_ended (FbdFeedbackVibraRumble *self) g_return_val_if_fail (FBD_IS_FEEDBACK_VIBRA_RUMBLE (self), G_SOURCE_REMOVE); if (self->periods) { - fbd_dev_vibra_rumble (dev, self->rumble, FALSE); + fbd_dev_vibra_rumble (dev, 1.0, self->rumble, FALSE); self->periods--; return G_SOURCE_CONTINUE; } @@ -130,7 +130,7 @@ fbd_feedback_vibra_rumble_start_vibra (FbdFeedbackVibra *vibra) g_debug ("Rumble Vibra event: duration %d, rumble: %d, pause: %d, period: %d", duration, self->rumble, self->pause, period); - fbd_dev_vibra_rumble (dev, self->rumble, TRUE); + fbd_dev_vibra_rumble (dev, 1.0, self->rumble, TRUE); self->periods--; if (self->periods) { self->timer_id = g_timeout_add (period, (GSourceFunc) on_period_ended, self); From 9cebb2c08122068a2685b1d320087e2d3599d11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:50 +0100 Subject: [PATCH 10/14] feedback-vibra: Add support for patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new class that takes an array of durations and magnitudes. A magnitude of 0 indicates a pause. We can consider dropping the VibraRumble class at some point as that is a special case of VibraPeriodic now however as the feedbacks are encoded in the themes let's make sure VibraPattern is good API before deprecating VibraRumble. Signed-off-by: Guido Günther --- src/fbd-feedback-vibra-pattern.c | 284 +++++++++++++++++++++++++++++++ src/fbd-feedback-vibra-pattern.h | 20 +++ src/fbd-feedback-vibra-rumble.c | 2 + src/meson.build | 1 + 4 files changed, 307 insertions(+) create mode 100644 src/fbd-feedback-vibra-pattern.c create mode 100644 src/fbd-feedback-vibra-pattern.h diff --git a/src/fbd-feedback-vibra-pattern.c b/src/fbd-feedback-vibra-pattern.c new file mode 100644 index 0000000..b74c44c --- /dev/null +++ b/src/fbd-feedback-vibra-pattern.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0+ + + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "fbd-feedback-vibra-pattern" + +#include "fbd-enums.h" +#include "fbd-feedback-vibra-pattern.h" +#include "fbd-feedback-vibra-priv.h" +#include "fbd-feedback-manager.h" + +/** + * FbdFeedbackVibraPattern: + * + * Describes a pattern feedback via a haptic motor + * + * The #FbdVibraVibraPattern describes the properties of a haptic feedback + * event. It knows nothing about the hardware itself but calls + * #FbdDevVibra for that. + */ + +enum { + PROP_0, + PROP_MAGNITUDES, + PROP_DURATIONS, + PROP_LAST_PROP, +}; +static GParamSpec *props[PROP_LAST_PROP]; + +typedef struct _FbdFeedbackVibraPattern { + FbdFeedbackVibra parent; + + GArray *magnitudes; + GArray *durations; + int pos; + + guint timer_id; +} FbdFeedbackVibraPattern; + +G_DEFINE_TYPE (FbdFeedbackVibraPattern, fbd_feedback_vibra_pattern, FBD_TYPE_FEEDBACK_VIBRA); + + +static void +set_magnitudes (FbdFeedbackVibraPattern *self, GArray *magnitudes) +{ + g_clear_pointer (&self->magnitudes, g_array_unref); + self->magnitudes = g_array_ref (magnitudes); +} + + +static void +set_durations (FbdFeedbackVibraPattern *self, GArray *durations) +{ + guint total_duration = 0; + + g_clear_pointer (&self->durations, g_array_unref); + self->durations = g_array_ref (durations); + + for (int i = 0; i < self->durations->len; i++) { + guint *duration = &g_array_index (self->durations, guint, i); + total_duration += *duration; + } + + fbd_feedback_vibra_set_duration (FBD_FEEDBACK_VIBRA (self), total_duration); +} + + +static void +fbd_feedback_vibra_pattern_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + FbdFeedbackVibraPattern *self = FBD_FEEDBACK_VIBRA_PATTERN (object); + + switch (property_id) { + case PROP_MAGNITUDES: + set_magnitudes (self, g_value_get_boxed (value)); + break; + case PROP_DURATIONS: + set_durations (self, g_value_get_boxed (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +fbd_feedback_vibra_pattern_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + FbdFeedbackVibraPattern *self = FBD_FEEDBACK_VIBRA_PATTERN (object); + + switch (property_id) { + case PROP_MAGNITUDES: + g_value_set_boxed (value, self->magnitudes); + break; + case PROP_DURATIONS: + g_value_set_boxed (value, self->durations); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void on_timer_expired (gpointer data); + + +static void +do_pattern_step (FbdFeedbackVibraPattern *self) +{ + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); + double *magnitude; + guint *duration; + + magnitude = &g_array_index (self->magnitudes, double, self->pos); + duration = &g_array_index (self->durations, guint, self->pos); + + g_debug ("step: pos: %d/%d, magn: %f, timeout %u", + self->pos, + self->durations->len, + *magnitude, + *duration); + + /* Removing any pending effect */ + fbd_dev_vibra_remove_effect (dev); + + if (*magnitude != 0.0) + fbd_dev_vibra_rumble (dev, *magnitude, *duration, TRUE); + + self->timer_id = g_timeout_add_once (*duration, + on_timer_expired, + self); + g_source_set_name_by_id (self->timer_id, "feedback-vibra-pattern-timer"); +} + + +static void +on_timer_expired (gpointer data) +{ + FbdFeedbackVibraPattern *self = data; + + g_return_if_fail (FBD_IS_FEEDBACK_VIBRA_PATTERN (self)); + g_return_if_fail (self->pos < self->durations->len); + + self->pos++; + + if (self->pos == self->durations->len) { + self->timer_id = 0; + return; + } + + do_pattern_step (self); +} + + +static void +fbd_feedback_vibra_pattern_end_vibra (FbdFeedbackVibra *vibra) +{ + FbdFeedbackVibraPattern *self = FBD_FEEDBACK_VIBRA_PATTERN (vibra); + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); + + if (self->timer_id) { + self->pos = 0; + g_clear_handle_id (&self->timer_id, g_source_remove); + } + + fbd_dev_vibra_stop (dev); +} + + +static void +fbd_feedback_vibra_pattern_start_vibra (FbdFeedbackVibra *vibra) +{ + FbdFeedbackVibraPattern *self = FBD_FEEDBACK_VIBRA_PATTERN (vibra); + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); + + g_return_if_fail (FBD_IS_DEV_VIBRA (dev)); + g_return_if_fail (self->magnitudes); + g_return_if_fail (self->durations); + g_return_if_fail (self->durations->len == self->magnitudes->len); + g_return_if_fail (self->pos == 0); + + g_debug ("Pattern Vibra: %u elements", self->durations->len); + + if (self->durations->len == 0) + return; + + do_pattern_step (self); +} + + +static gboolean +fbd_feedback_vibra_pattern_is_available (FbdFeedbackBase *base) +{ + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); + + return FBD_IS_DEV_VIBRA (dev); +} + + +static void +fbd_feedback_vibra_pattern_finalize (GObject *object) +{ + FbdFeedbackVibraPattern *self = FBD_FEEDBACK_VIBRA_PATTERN (object); + + fbd_feedback_vibra_pattern_end_vibra (FBD_FEEDBACK_VIBRA (self)); + g_clear_pointer (&self->magnitudes, g_array_unref); + g_clear_pointer (&self->durations, g_array_unref); + + G_OBJECT_CLASS (fbd_feedback_vibra_pattern_parent_class)->finalize (object); +} + + +static void +fbd_feedback_vibra_pattern_class_init (FbdFeedbackVibraPatternClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + FbdFeedbackBaseClass *base_class = FBD_FEEDBACK_BASE_CLASS (klass); + FbdFeedbackVibraClass *vibra_class = FBD_FEEDBACK_VIBRA_CLASS (klass); + + object_class->finalize = fbd_feedback_vibra_pattern_finalize; + object_class->set_property = fbd_feedback_vibra_pattern_set_property; + object_class->get_property = fbd_feedback_vibra_pattern_get_property; + + base_class->is_available = fbd_feedback_vibra_pattern_is_available; + + vibra_class->start_vibra = fbd_feedback_vibra_pattern_start_vibra; + vibra_class->end_vibra = fbd_feedback_vibra_pattern_end_vibra; + + /** + * FbdFeedbackVibraPattern:magnitudes + * + * The magnitudes as array of doubles. The values specify + * the relative magnitude of a rumble. A value of 0 indicates + * a pause. + */ + props[PROP_MAGNITUDES] = + g_param_spec_boxed ("magnitudes", "", "", + G_TYPE_ARRAY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + /** + * FbdFeedbackVibraPattern:durations + * + * The durations as array of unsigned integers. The values specify + * an the length of each rumble. + */ + props[PROP_DURATIONS] = + g_param_spec_boxed ("durations", "", "", + G_TYPE_ARRAY, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, PROP_LAST_PROP, props); +} + + +static void +fbd_feedback_vibra_pattern_init (FbdFeedbackVibraPattern *self) +{ +} + + +FbdFeedbackVibraPattern * +fbd_feedback_vibra_pattern_new (GArray *magnitudes, GArray *durations) +{ + return g_object_new (FBD_TYPE_FEEDBACK_VIBRA_PATTERN, + "magnitudes", magnitudes, + "durations", durations, + NULL); +} diff --git a/src/fbd-feedback-vibra-pattern.h b/src/fbd-feedback-vibra-pattern.h new file mode 100644 index 0000000..170905b --- /dev/null +++ b/src/fbd-feedback-vibra-pattern.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0+ + */ +#pragma once + +#include "fbd-feedback-vibra.h" + +G_BEGIN_DECLS + +#define FBD_TYPE_FEEDBACK_VIBRA_PATTERN (fbd_feedback_vibra_pattern_get_type()) + +G_DECLARE_FINAL_TYPE (FbdFeedbackVibraPattern, fbd_feedback_vibra_pattern, FBD, + FEEDBACK_VIBRA_PATTERN, + FbdFeedbackVibra); + +FbdFeedbackVibraPattern *fbd_feedback_vibra_pattern_new (GArray *magnitudes, GArray *durations); + +G_END_DECLS diff --git a/src/fbd-feedback-vibra-rumble.c b/src/fbd-feedback-vibra-rumble.c index 534c9a1..e81b097 100644 --- a/src/fbd-feedback-vibra-rumble.c +++ b/src/fbd-feedback-vibra-rumble.c @@ -18,6 +18,8 @@ * The #FbdVibraVibraRumble describes the properties of a haptic feedback * event. It knows nothing about the hardware itself but calls * #FbdDevVibra for that. + * + * TODO: could be dropped in favor of the more flexible `FbdFeedbackVibraPattern`. */ enum { diff --git a/src/meson.build b/src/meson.build index 1efea59..8493aab 100644 --- a/src/meson.build +++ b/src/meson.build @@ -30,6 +30,7 @@ sources = [ 'fbd-feedback-sound.c', 'fbd-feedback-theme.c', 'fbd-feedback-vibra.c', + 'fbd-feedback-vibra-pattern.c', 'fbd-feedback-vibra-periodic.c', 'fbd-feedback-vibra-rumble.c', 'fbd-theme-expander.c', From 93dc859f7d6dc6c905337b102c030cc399b47b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:51 +0100 Subject: [PATCH 11/14] data: Add vibration API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The API is meant to trigger the given vibration pattern on the haptic device. The pattern is sequence of relative amplitude and duration pairs. A magnitude of 0.0 indicates a pause. Signed-off-by: Guido Günther --- data/meson.build | 5 ++++- data/org.sigxcpu.Feedback.Haptic.xml | 32 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 data/org.sigxcpu.Feedback.Haptic.xml diff --git a/data/meson.build b/data/meson.build index 7ff25cc..728989d 100644 --- a/data/meson.build +++ b/data/meson.build @@ -4,7 +4,10 @@ namespace = 'LfbGdbus' generated_dbus_sources = [] -dbus_interfaces = [ 'org.sigxcpu.Feedback.xml' ] +dbus_interfaces = [ + 'org.sigxcpu.Feedback.xml', + 'org.sigxcpu.Feedback.Haptic.xml', +] generated_dbus_sources += gnome.gdbus_codegen('lfb-gdbus', sources : dbus_interfaces, diff --git a/data/org.sigxcpu.Feedback.Haptic.xml b/data/org.sigxcpu.Feedback.Haptic.xml new file mode 100644 index 0000000..ea6c4ee --- /dev/null +++ b/data/org.sigxcpu.Feedback.Haptic.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + From 31c7bf7d6b1022ed5a3eac198370aa128a40dc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:51 +0100 Subject: [PATCH 12/14] haptic-manager: New object to handle the Haptic DBus interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To trigger a 200ms rumble at 100%, a 50ms pause and a 300ms rumble at 50%: busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 3 1.0 200 0.0 50 0.5 300 To cancel an ongoing pattern: busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 0 The API has lower priority than the event based API to e.g. not miss incoming phone calls or cell broadast messages. Signed-off-by: Guido Günther --- src/fbd-haptic-manager.c | 200 +++++++++++++++++++++++++++++++++++++++ src/fbd-haptic-manager.h | 23 +++++ src/meson.build | 1 + 3 files changed, 224 insertions(+) create mode 100644 src/fbd-haptic-manager.c create mode 100644 src/fbd-haptic-manager.h diff --git a/src/fbd-haptic-manager.c b/src/fbd-haptic-manager.c new file mode 100644 index 0000000..a886f35 --- /dev/null +++ b/src/fbd-haptic-manager.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Author: Guido Günther + */ + +#define G_LOG_DOMAIN "fbd-haptic-manager" + +#include "fbd-feedback-manager.h" +#include "fbd-haptic-manager.h" +#include "fbd-feedback-base.h" +#include "fbd-feedback-profile.h" +#include "fbd-feedback-vibra-pattern.h" + +#include + +#define MAX_ITEMS 10 +#define MAX_LEN 10000 + +/** + * FbdHapticManager: + * + * Handles the org.sigxcpu.Feedback.Haptic interface + */ + +struct _FbdHapticManager { + LfbGdbusFeedbackHapticSkeleton parent; + + FbdFeedbackVibraPattern *vibra; +}; + +static void fbd_feedback_manager_feedback_haptic_iface_init (LfbGdbusFeedbackHapticIface *iface); + +G_DEFINE_TYPE_WITH_CODE (FbdHapticManager, + fbd_haptic_manager, + LFB_GDBUS_TYPE_FEEDBACK_HAPTIC_SKELETON, + G_IMPLEMENT_INTERFACE ( + LFB_GDBUS_TYPE_FEEDBACK_HAPTIC, + fbd_feedback_manager_feedback_haptic_iface_init)); + + +/** + * build_pattern: + * @pattern: The pattern as GVariant + * @out_magnitudes:(out): The magnitudes + * @out_durations: The durations + * + * Parses the pattern (applying the limits) into an array of magnitudes + * and durations. + * + * Returns: The `TRUE` if here were elements in the pattern, otherwise `FALSE` + */ +static gboolean +build_pattern (GVariant *pattern, GArray **out_magnitudes, GArray **out_durations) +{ + GVariantIter iter; + GArray *durations, *magnitudes; + double magnitude; + guint duration, n_items; + + g_variant_iter_init (&iter, pattern); + + n_items = MIN (g_variant_iter_n_children (&iter), MAX_ITEMS); + + if (n_items == 0) + return FALSE; + + durations = g_array_sized_new (FALSE, FALSE, sizeof (guint), n_items); + magnitudes = g_array_sized_new (FALSE, FALSE, sizeof (double), n_items); + + for (guint pos = 0; pos < n_items; pos++) { + g_variant_iter_next (&iter, "(du)", &magnitude, &duration); + + duration = MIN (duration, MAX_LEN); + g_array_append_val (durations, duration); + + magnitude = MAX(0.0, MIN (magnitude, 1.0)); + g_array_append_val (magnitudes, magnitude); + } + + *out_durations = durations; + *out_magnitudes = magnitudes; + + return TRUE; +} + + +static gboolean +fbd_feedback_manager_handle_vibrate (LfbGdbusFeedbackHaptic *object, + GDBusMethodInvocation *invocation, + const gchar *app_id, + GVariant *pattern) +{ + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdHapticManager *self = FBD_HAPTIC_MANAGER (object); + FbdDevVibra *vibra_dev; + FbdFeedbackProfileLevel level; + gboolean non_empty; + g_autoptr (FbdFeedbackVibraPattern) fb = NULL; + g_autoptr (GArray) magnitudes = NULL, durations = NULL; + + g_debug ("Haptic triggered for %s", app_id); + + level = fbd_feedback_manager_get_effective_level (manager, + app_id, + FBD_FEEDBACK_PROFILE_LEVEL_QUIET, + FALSE); + if (level < FBD_FEEDBACK_PROFILE_LEVEL_QUIET) { + g_debug ("Feedback level too low for haptic"); + lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE); + return TRUE; + } + + vibra_dev = fbd_feedback_manager_get_dev_vibra (manager); + if (!vibra_dev) { + g_debug ("No haptic device"); + lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE); + return TRUE; + } + + non_empty = build_pattern (pattern, &magnitudes, &durations); + if (!non_empty) { + g_debug ("Empty pattern, ending feedback"); + fbd_haptic_manager_end_feedback (self); + g_clear_object (&self->vibra); + lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, TRUE); + return TRUE; + } + + if (self->vibra) + fbd_feedback_end (FBD_FEEDBACK_BASE (self->vibra)); + if (fbd_dev_vibra_is_busy (vibra_dev)) { + g_debug ("Haptic busy"); + /* If there's an event with haptic deny haptic patter */ + lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE); + return TRUE; + } + + fb = fbd_feedback_vibra_pattern_new (magnitudes, durations); + fbd_feedback_run (FBD_FEEDBACK_BASE (fb)); + self->vibra = g_steal_pointer (&fb); + + lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, TRUE); + return TRUE; +} + + +static void +fbd_feedback_manager_feedback_haptic_iface_init (LfbGdbusFeedbackHapticIface *iface) +{ + iface->handle_vibrate = fbd_feedback_manager_handle_vibrate; +} + + +static void +fbd_haptic_manager_finalize (GObject *object) +{ + FbdHapticManager *self = FBD_HAPTIC_MANAGER (object); + + fbd_haptic_manager_end_feedback (self); + + G_OBJECT_CLASS (fbd_haptic_manager_parent_class)->finalize (object); +} + + +static void +fbd_haptic_manager_class_init (FbdHapticManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = fbd_haptic_manager_finalize; +} + + +static void +fbd_haptic_manager_init (FbdHapticManager *self) +{ +} + + +FbdHapticManager * +fbd_haptic_manager_new (void) +{ + return g_object_new (FBD_TYPE_HAPTIC_MANAGER, NULL); +} + + +void +fbd_haptic_manager_end_feedback (FbdHapticManager *self) +{ + g_return_if_fail (FBD_IS_HAPTIC_MANAGER (self)); + + if (!self->vibra) + return; + + fbd_feedback_end (FBD_FEEDBACK_BASE (self->vibra)); + g_clear_object (&self->vibra); +} diff --git a/src/fbd-haptic-manager.h b/src/fbd-haptic-manager.h new file mode 100644 index 0000000..3579184 --- /dev/null +++ b/src/fbd-haptic-manager.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Phosh Developers + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include "lfb-gdbus.h" +#include + +G_BEGIN_DECLS + +#define FBD_TYPE_HAPTIC_MANAGER (fbd_haptic_manager_get_type ()) + +G_DECLARE_FINAL_TYPE (FbdHapticManager, fbd_haptic_manager, FBD, HAPTIC_MANAGER, + LfbGdbusFeedbackHapticSkeleton) + +FbdHapticManager *fbd_haptic_manager_new (void); +void fbd_haptic_manager_end_feedback (FbdHapticManager *self); + + +G_END_DECLS diff --git a/src/meson.build b/src/meson.build index 8493aab..8f071a7 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ sources = [ 'fbd-feedback-vibra-pattern.c', 'fbd-feedback-vibra-periodic.c', 'fbd-feedback-vibra-rumble.c', + 'fbd-haptic-manager.c', 'fbd-theme-expander.c', 'fbd-udev.c', ] From f639f7d103687401a884402838874d9236bf9a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:53 +0100 Subject: [PATCH 13/14] feedback-manager: Instantiate .Haptic DBus interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the haptic interface available on the Bus when a haptic motor is found at daemon startup. Closes: https://source.puri.sm/Librem5/feedbackd/-/issues/80 Signed-off-by: Guido Günther --- src/fbd-feedback-manager.c | 42 ++++++++++++++++++++++++++++++++++---- src/fbd-feedback-manager.h | 8 ++++++++ src/fbd.c | 38 +++++++++++++++++++++++++++++----- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/fbd-feedback-manager.c b/src/fbd-feedback-manager.c index 7bfac52..44b934c 100644 --- a/src/fbd-feedback-manager.c +++ b/src/fbd-feedback-manager.c @@ -12,8 +12,10 @@ #include "fbd-dev-leds.h" #include "fbd-event.h" #include "fbd-feedback-vibra.h" +#include "fbd-feedback-vibra-pattern.h" #include "fbd-feedback-manager.h" #include "fbd-feedback-theme.h" +#include "fbd-haptic-manager.h" #include "fbd-theme-expander.h" #include @@ -40,6 +42,9 @@ * based on the incoming events. */ +FbdDebugFlags fbd_debug_flags; + + typedef struct _FbdFeedbackManager { LfbGdbusFeedbackSkeleton parent; @@ -54,6 +59,9 @@ typedef struct _FbdFeedbackManager { /* Key: DBus name, value: watch_id */ GHashTable *clients; + /* org.sigxcpu.Feedbackd.Haptic */ + FbdHapticManager *haptic_manager; + /* Hardware interaction */ GUdevClient *client; FbdDevVibra *vibra; @@ -410,15 +418,27 @@ fbd_feedback_manager_handle_trigger_feedback (LfbGdbusFeedback *object, feedbacks = fbd_feedback_theme_lookup_feedback (self->theme, level, event); if (feedbacks) { + gboolean has_vibra = FALSE; + for (l = feedbacks; l; l = l->next) { FbdFeedbackBase *fb = FBD_FEEDBACK_BASE (l->data); if (fbd_feedback_is_available (FBD_FEEDBACK_BASE (fb))) { - /* Handle one haptic feedback at a time */ - /* TODO: should use priorities */ - if (FBD_IS_FEEDBACK_VIBRA (fb) && fbd_dev_vibra_is_busy (self->vibra)) - continue; + /* Handle one haptic feedback at a time. In practice haptics can handle multiple + * patterns but none of the devices supports this atm */ + /* TODO: should respect priorities */ + if (FBD_IS_FEEDBACK_VIBRA (fb)) { + if (fbd_dev_vibra_is_busy (self->vibra)) + continue; + has_vibra = TRUE; + } + fbd_event_add_feedback (event, fb); + + /* Events take priority over the haptic interface */ + if (has_vibra) + fbd_haptic_manager_end_feedback (self->haptic_manager); + found_fb = TRUE; } } @@ -494,6 +514,9 @@ fbd_feedback_manager_constructed (GObject *object) g_signal_connect_swapped (self->settings, "changed::" FEEDBACKD_KEY_ALLOW_IMPORTANT, G_CALLBACK (on_feedbackd_allow_important_changed), self); on_feedbackd_allow_important_changed (self, FEEDBACKD_KEY_ALLOW_IMPORTANT, self->settings); + + if (self->vibra || fbd_debug_flags & FBD_DEBUG_FLAG_FORCE_HAPTIC) + self->haptic_manager = fbd_haptic_manager_new (); } @@ -502,6 +525,8 @@ fbd_feedback_manager_dispose (GObject *object) { FbdFeedbackManager *self = FBD_FEEDBACK_MANAGER (object); + g_clear_object (&self->haptic_manager); + g_clear_object (&self->settings); g_clear_object (&self->theme); g_clear_object (&self->sound); @@ -662,6 +687,15 @@ fbd_feedback_manager_set_profile (FbdFeedbackManager *self, const gchar *profile return TRUE; } + +FbdHapticManager * +fbd_feedback_manager_get_haptic_manager (FbdFeedbackManager *self) +{ + g_assert (FBD_IS_FEEDBACK_MANAGER (self)); + + return self->haptic_manager; +} + /** * fbd_feedback_manager_get_effective_level: * @self: The feedback manager diff --git a/src/fbd-feedback-manager.h b/src/fbd-feedback-manager.h index 7614333..19308ef 100644 --- a/src/fbd-feedback-manager.h +++ b/src/fbd-feedback-manager.h @@ -9,17 +9,25 @@ #include "fbd-dev-leds.h" #include "fbd-dev-sound.h" #include "fbd-feedback-profile.h" +#include "fbd-haptic-manager.h" #include "lfb-gdbus.h" #include G_BEGIN_DECLS +typedef enum { + FBD_DEBUG_FLAG_FORCE_HAPTIC = 1 << 0, +} FbdDebugFlags; + +extern FbdDebugFlags fbd_debug_flags; + #define FBD_TYPE_FEEDBACK_MANAGER (fbd_feedback_manager_get_type()) G_DECLARE_FINAL_TYPE (FbdFeedbackManager, fbd_feedback_manager, FBD, FEEDBACK_MANAGER, LfbGdbusFeedbackSkeleton); FbdFeedbackManager *fbd_feedback_manager_get_default (void); +FbdHapticManager *fbd_feedback_manager_get_haptic_manager (FbdFeedbackManager *self); FbdDevVibra *fbd_feedback_manager_get_dev_vibra (FbdFeedbackManager *self); FbdDevSound *fbd_feedback_manager_get_dev_sound (FbdFeedbackManager *self); FbdDevLeds *fbd_feedback_manager_get_dev_leds (FbdFeedbackManager *self); diff --git a/src/fbd.c b/src/fbd.c index b1bb320..655d05a 100644 --- a/src/fbd.c +++ b/src/fbd.c @@ -8,15 +8,24 @@ #include "fbd.h" #include "fbd-feedback-manager.h" +#include "fbd-haptic-manager.h" #include "lfb-names.h" #include "lfb-gdbus.h" #include #include - static GMainLoop *loop; + +static GDebugKey debug_keys[] = +{ + { .key = "force-haptic", + .value = FBD_DEBUG_FLAG_FORCE_HAPTIC, + }, +}; + + static gboolean quit_cb (gpointer user_data) { @@ -45,17 +54,29 @@ reload_cb (gpointer user_data) static void bus_acquired_cb (GDBusConnection *connection, - const gchar *name, - gpointer user_data) + const gchar *name, + gpointer user_data) { FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + FbdHapticManager *haptic_manager; - g_debug ("Bus acquired, creating manager..."); + g_assert (FBD_IS_FEEDBACK_MANAGER (manager)); + + g_debug ("Bus acquired, exporting manager..."); g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager), connection, FB_DBUS_PATH, NULL); + + haptic_manager = fbd_feedback_manager_get_haptic_manager (manager); + if (haptic_manager) { + g_debug ("Exporting haptic manager..."); + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (haptic_manager), + connection, + FB_DBUS_PATH, + NULL); + } } @@ -88,11 +109,13 @@ name_lost_cb (GDBusConnection *connection, } -int main(int argc, char *argv[]) +int +main (int argc, char *argv[]) { g_autoptr(GError) err = NULL; g_autoptr(GOptionContext) opt_context = NULL; g_autoptr (FbdFeedbackManager) manager = NULL; + const char *debugenv; opt_context = g_option_context_new ("- A daemon to trigger event feedback"); if (!g_option_context_parse (opt_context, &argc, &argv, &err)) { @@ -101,6 +124,11 @@ int main(int argc, char *argv[]) return 1; } + debugenv = g_getenv ("FEEDBACKD_DEBUG"); + fbd_debug_flags = g_parse_debug_string (debugenv, + debug_keys, + G_N_ELEMENTS (debug_keys)); + manager = fbd_feedback_manager_get_default (); fbd_feedback_manager_load_theme (manager); From 7f2283e64af4f54203d1b93056a3cb916a02e39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Wed, 11 Dec 2024 12:46:53 +0100 Subject: [PATCH 14/14] README: Document new API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guido Günther --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index b7fc78e..78e6569 100644 --- a/README.md +++ b/README.md @@ -277,11 +277,32 @@ export GSETTINGS_SCHEMA_DIR=_build/data/ gsettings set org.sigxcpu.feedbackd.application:/org/sigxcpu/feedbackd/application/sm-puri-phosh/ profile quiet ``` +## Haptic API + +In order to give applications like browsers and games more control +over the haptic motor `Feebackd` implements an interface that allows +to set vibra patterns. The vibration pattern is given as a sequence of +rumble magnitude and duration pairs like: + +``` +busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 3 1.0 200 0.0 50 0.5 300 +``` + +Vibration can be ended by submitting an empty array: + +```sh +busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 0 +``` + +The API is exported as a separate interface `org.sigxcpu.Feedback.Haptic` which is only available when +a haptic device is found. + ## Documentation - [Libfeedback API](https://honk.sigxcpu.org/projects/feedbackd/doc/) - [Event naming spec draft](./doc/Event-naming-spec-0.0.0.md) - [Feedback-theme-spec draft](./doc/Feedback-theme-spec-0.0.0.md) +- [W3's vibration API draft](https://www.w3.org/TR/vibration/) [debian/control]: ./debian/control#L5 [1]: https://source.puri.sm/Librem5/feedbackd-device-themes