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] 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',