diff --git a/data/default.json b/data/default.json index 4be8cec..f1a4c40 100644 --- a/data/default.json +++ b/data/default.json @@ -97,10 +97,8 @@ { "event-name" : "alarm-clock-elapsed", "type" : "VibraPeriodic", - "magnitude" : 32767, - "duration" : 10000, - "fade-in-time" : 10000, - "fade-in-level": 4096 + "magnitude" : 0.5, + "duration" : 1000 }, { "event-name" : "bell-terminal", @@ -120,9 +118,9 @@ }, { "event-name" : "message-new-cellbroadcast", - "type" : "VibraPeriodic", - "magnitude" : 10000, - "duration" : 3000 + "type" : "VibraPattern", + "magnitudes" : [1.0, 0.0], + "durations" : [200, 200] }, { "event-name" : "message-new-email", @@ -157,7 +155,8 @@ { "event-name" : "phone-incoming-call", "type" : "VibraPeriodic", - "duration" : 10000 + "magnitude" : 0.5, + "duration" : 1000 }, { "event-name" : "timeout-completed", @@ -167,8 +166,8 @@ { "event-name" : "theme-demo", "type" : "VibraPeriodic", - "magnitude" : 32767, - "duration" : 5000 + "magnitude" : 0.5, + "duration" : 1000 }, { "event-name" : "window-close", diff --git a/doc/feedbackd.rst b/doc/feedbackd.rst index d7713d1..5894c65 100644 --- a/doc/feedbackd.rst +++ b/doc/feedbackd.rst @@ -30,6 +30,8 @@ app that triggered it crashes. For details refer to the event and feedback theme specs at ``__ +Feedbackd reloads the feedback theme on `SIGHUP` (i.e. `pkill -HUP feedbackd`). + Options ======= diff --git a/src/fbd-dev-vibra.c b/src/fbd-dev-vibra.c index 85368a4..f764a6c 100644 --- a/src/fbd-dev-vibra.c +++ b/src/fbd-dev-vibra.c @@ -272,33 +272,27 @@ fbd_dev_vibra_rumble (FbdDevVibra *self, double magnitude, guint duration, gbool /* 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) +fbd_dev_vibra_periodic (FbdDevVibra *self, + guint duration, + double magnitude, + double fade_in_level, + guint fade_in_time) { struct input_event event; struct ff_effect effect = { 0 }; g_return_val_if_fail (FBD_IS_DEV_VIBRA (self), FALSE); - if (!magnitude) - magnitude = 0x7FFF; - - if (!fade_in_level) - fade_in_level = magnitude; - - if (!fade_in_time) - fade_in_time = duration; - effect.type = FF_PERIODIC; effect.id = -1; effect.u.periodic.waveform = FF_SINE; effect.u.periodic.period = 10; - effect.u.periodic.magnitude = magnitude; + effect.u.periodic.magnitude = 0x7FFF * magnitude; effect.u.periodic.offset = 0; effect.u.periodic.phase = 0; effect.direction = 0x4000; effect.u.periodic.envelope.attack_length = fade_in_time; - effect.u.periodic.envelope.attack_level = fade_in_level;; + effect.u.periodic.envelope.attack_level = 0x7FFF * fade_in_level; effect.u.periodic.envelope.fade_length = 0; effect.u.periodic.envelope.fade_level = 0; effect.trigger.button = 0; diff --git a/src/fbd-dev-vibra.h b/src/fbd-dev-vibra.h index ef5891b..3cf3e3b 100644 --- a/src/fbd-dev-vibra.h +++ b/src/fbd-dev-vibra.h @@ -19,8 +19,11 @@ 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_periodic (FbdDevVibra *self, + guint duration, + double magnitude, + double fade_in_level, + guint fade_in_time); gboolean fbd_dev_vibra_stop (FbdDevVibra *self); gboolean fbd_dev_vibra_remove_effect (FbdDevVibra *self); GUdevDevice *fbd_dev_vibra_get_device(FbdDevVibra *self); diff --git a/src/fbd-feedback-vibra-periodic.c b/src/fbd-feedback-vibra-periodic.c index bb7c69f..f1b81ff 100644 --- a/src/fbd-feedback-vibra-periodic.c +++ b/src/fbd-feedback-vibra-periodic.c @@ -11,18 +11,18 @@ #include "fbd-feedback-vibra-periodic.h" #include "fbd-feedback-manager.h" +#include + /** - * SECTION:fbd-feedback-vibra-periodic - * @short_description: Describes a periodic feedback via a haptic motor - * @Title: FbdFeedbackVibraPeriodic + * FbdFeedbackVibraPeriodic: + * + * Describes a periodic feedback via a haptic motor * * The #FbdVibraVibraPeriodic describes the properties of a haptic feedback * event. It knows nothing about the hardware itself but calls * #FbdDevVibra for that. */ -#define MAX_MAGNITUDE 0xFFFF - enum { PROP_0, PROP_MAGNITUDE, @@ -35,27 +35,73 @@ static GParamSpec *props[PROP_LAST_PROP]; typedef struct _FbdFeedbackVibraPeriodic { FbdFeedbackVibra parent; - guint magnitude; - guint fade_in_level; - guint fade_in_time; + double magnitude; + double fade_in_level; + guint fade_in_time; } FbdFeedbackVibraPeriodic; -G_DEFINE_TYPE (FbdFeedbackVibraPeriodic, fbd_feedback_vibra_periodic, FBD_TYPE_FEEDBACK_VIBRA); +static void json_serializable_iface_init (JsonSerializableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (FbdFeedbackVibraPeriodic, fbd_feedback_vibra_periodic, + FBD_TYPE_FEEDBACK_VIBRA, + G_IMPLEMENT_INTERFACE (JSON_TYPE_SERIALIZABLE, + json_serializable_iface_init)); + +static gboolean +fbd_feedback_vibra_periodic_serializable_deserialize_property (JsonSerializable *serializable, + const gchar *property_name, + GValue *value, + GParamSpec *pspec, + JsonNode *property_node) +{ + if (g_strcmp0 (property_name, "magnitude") == 0) { + if (JSON_NODE_TYPE (property_node) == JSON_NODE_VALUE) { + double magnitude = json_node_get_double (property_node); + + /* Backward compat handling: Initially magnitude was given as [0, 0xFFFF] */ + if (magnitude > 1.0) { + g_warning_once ("Too large magnitude %.2f detected, please update the theme", magnitude); + magnitude = magnitude / 0xFFFF; + } + + + g_value_set_double (value, magnitude); + return TRUE; + } + } else if (g_strcmp0 (property_name, "fade-in-level") == 0) { + if (JSON_NODE_TYPE (property_node) == JSON_NODE_VALUE) { + double fade_in_level = json_node_get_double (property_node); + + /* Backward compat handling: Initially duration was given as [0, 0xFFFF] */ + if (fade_in_level > 1.0) { + g_warning_once ("Too large fade-in-level %.2f detected, please update the theme", + fade_in_level); + fade_in_level = fade_in_level / 0xFFFF; + } + + g_value_set_double (value, fade_in_level); + return TRUE; + } + } + + return FALSE; +} + static void fbd_feedback_vibra_periodic_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) + guint property_id, + const GValue *value, + GParamSpec *pspec) { FbdFeedbackVibraPeriodic *self = FBD_FEEDBACK_VIBRA_PERIODIC (object); switch (property_id) { case PROP_MAGNITUDE: - self->magnitude = g_value_get_uint (value); + self->magnitude = g_value_get_double (value); break; case PROP_FADE_IN_LEVEL: - self->fade_in_level = g_value_get_uint (value); + self->fade_in_level = g_value_get_double (value); break; case PROP_FADE_IN_TIME: self->fade_in_time = g_value_get_uint (value); @@ -68,18 +114,18 @@ fbd_feedback_vibra_periodic_set_property (GObject *object, static void fbd_feedback_vibra_periodic_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) + guint property_id, + GValue *value, + GParamSpec *pspec) { FbdFeedbackVibraPeriodic *self = FBD_FEEDBACK_VIBRA_PERIODIC (object); switch (property_id) { case PROP_MAGNITUDE: - g_value_set_uint (value, self->magnitude); + g_value_set_double (value, self->magnitude); break; case PROP_FADE_IN_LEVEL: - g_value_set_uint (value, self->fade_in_level); + g_value_set_double (value, self->fade_in_level); break; case PROP_FADE_IN_TIME: g_value_set_uint (value, self->fade_in_time); @@ -107,15 +153,15 @@ fbd_feedback_vibra_periodic_start_vibra (FbdFeedbackVibra *vibra) FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager); guint duration = fbd_feedback_vibra_get_duration (vibra); double max_strength = fbd_feedback_vibra_get_max_strength (FBD_FEEDBACK_VIBRA (self)); - double fade_in_ratio = (double)self->fade_in_level / self->magnitude; - guint max_magnitude, fade_in_level; + double fade_in_ratio = self->fade_in_level / self->magnitude; + double max_magnitude, fade_in_level; g_return_if_fail (FBD_IS_DEV_VIBRA (dev)); - max_magnitude = MIN (MAX_MAGNITUDE * max_strength, self->magnitude); + max_magnitude = MIN (max_strength, self->magnitude); fade_in_level = MIN (max_magnitude * fade_in_ratio, self->fade_in_level); - g_debug ("Periodic Vibra: %d %d %d %d", - duration, max_magnitude, fade_in_level, self->fade_in_time); + g_debug ("Periodic Vibra: (%f,%d) (%f,%d)", + max_magnitude, duration, fade_in_level, self->fade_in_time); fbd_dev_vibra_periodic (dev, duration, max_magnitude, fade_in_level, self->fade_in_time); } @@ -129,6 +175,14 @@ fbd_feedback_vibra_periodic_is_available (FbdFeedbackBase *base) return FBD_IS_DEV_VIBRA (dev); } + +static void +json_serializable_iface_init (JsonSerializableIface *iface) +{ + iface->deserialize_property = fbd_feedback_vibra_periodic_serializable_deserialize_property; +} + + static void fbd_feedback_vibra_periodic_class_init (FbdFeedbackVibraPeriodicClass *klass) { @@ -145,28 +199,19 @@ fbd_feedback_vibra_periodic_class_init (FbdFeedbackVibraPeriodicClass *klass) vibra_class->end_vibra = fbd_feedback_vibra_periodic_end_vibra; props[PROP_MAGNITUDE] = - g_param_spec_uint ( - "magnitude", - "Magnitude", - "total magnitude", - 0, MAX_MAGNITUDE, 0x7FFF, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_param_spec_double ("magnitude", "", "", + 0.0, 1.0, 0.5, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_FADE_IN_TIME] = - g_param_spec_uint ( - "fade-in-time", - "Fade in time", - "Fade in time until full strength", - 0, G_MAXINT, 0, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_param_spec_uint ("fade-in-time", "", "", + 0, G_MAXINT, 500, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); props[PROP_FADE_IN_LEVEL] = - g_param_spec_uint ( - "fade-in-level", - "Fade in level", - "Fade in start level", - 0, MAX_MAGNITUDE, 0x7FFF, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); + g_param_spec_double ("fade-in-level", "", "", + 0.0, 1.0, 0.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST_PROP, props); } @@ -174,4 +219,7 @@ fbd_feedback_vibra_periodic_class_init (FbdFeedbackVibraPeriodicClass *klass) static void fbd_feedback_vibra_periodic_init (FbdFeedbackVibraPeriodic *self) { + self->magnitude = 0.5; + self->fade_in_level = 0.0; + self->fade_in_time = 500; } diff --git a/tests/test-fbd-feedback-vibra.c b/tests/test-fbd-feedback-vibra.c index 73e4dac..d0c14b8 100644 --- a/tests/test-fbd-feedback-vibra.c +++ b/tests/test-fbd-feedback-vibra.c @@ -9,6 +9,7 @@ #define GMOBILE_USE_UNSTABLE_API #include "fbd-feedback-manager.h" #include "fbd-feedback-vibra-pattern.h" +#include "fbd-feedback-vibra-periodic.h" #include "fbd-feedback-vibra-rumble.h" #include @@ -95,6 +96,86 @@ test_fbd_feedback_vibra_pattern (void) } +static void +test_fbd_feedback_vibra_periodic (void) +{ + /* Create manager upfront so we can dispose it, otherwise creating + * any feedback would create it implicitly */ + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + g_autoptr (GError) err = NULL; + g_autoptr (JsonNode) node = NULL; + GObject *object; + double magnitude, fade_in_level; + guint fade_in_time; + + node = json_from_string("{" + " \"event-name\" : \"button-pressed\"," + " \"type\" : \"VibraPeriodic\"," + " \"magnitude\" : 0.7," + " \"fade-in-level\" : 0.3" + "}", &err); + g_assert_no_error (err); + + object = json_gobject_deserialize (FBD_TYPE_FEEDBACK_VIBRA_PERIODIC, node); + g_object_get (object, + "magnitude", &magnitude, + "fade-in-level", &fade_in_level, + "fade-in-time", &fade_in_time, + NULL); + + g_assert_cmpfloat_with_epsilon (magnitude, 0.7, FLT_EPSILON); + g_assert_cmpfloat_with_epsilon (fade_in_level, 0.3, FLT_EPSILON); + g_assert_cmpint (fade_in_time, ==, 500); + + g_assert_finalize_object (object); + g_assert_finalize_object (manager); +} + + +static void +test_fbd_feedback_vibra_periodic_fallback (void) +{ + /* Create manager upfront so we can dispose it, otherwise creating + * any feedback would create it implicitly */ + FbdFeedbackManager *manager = fbd_feedback_manager_get_default (); + g_autoptr (GError) err = NULL; + g_autoptr (JsonNode) node = NULL; + GObject *object; + double magnitude, fade_in_level; + guint fade_in_time; + + node = json_from_string("{" + " \"event-name\" : \"button-pressed\"," + " \"type\" : \"VibraPeriodic\"," + " \"magnitude\" : 65535," + " \"fade-in-level\" : 13107" + "}", &err); + g_assert_no_error (err); + + g_test_expect_message ("fbd-feedback-vibra-periodic", + G_LOG_LEVEL_WARNING, + "Too large magnitude 65535.00 detected*"); + g_test_expect_message ("fbd-feedback-vibra-periodic", + G_LOG_LEVEL_WARNING, + "Too large fade-in-level 13107.00 detected*"); + object = json_gobject_deserialize (FBD_TYPE_FEEDBACK_VIBRA_PERIODIC, node); + g_test_assert_expected_messages (); + + g_object_get (object, + "magnitude", &magnitude, + "fade-in-level", &fade_in_level, + "fade-in-time", &fade_in_time, + NULL); + + g_assert_cmpfloat_with_epsilon (magnitude, 1.0, FLT_EPSILON); + g_assert_cmpfloat_with_epsilon (fade_in_level, 0.2, FLT_EPSILON); + g_assert_cmpint (fade_in_time, ==, 500); + + g_assert_finalize_object (object); + g_assert_finalize_object (manager); +} + + gint main (gint argc, gchar *argv[]) { @@ -102,6 +183,9 @@ main (gint argc, gchar *argv[]) g_test_add_func("/feedbackd/fbd/feedback-vibra/rumble", test_fbd_feedback_vibra_rumble); g_test_add_func("/feedbackd/fbd/feedback-vibra/pattern", test_fbd_feedback_vibra_pattern); + g_test_add_func("/feedbackd/fbd/feedback-vibra/periodic", test_fbd_feedback_vibra_periodic); + g_test_add_func("/feedbackd/fbd/feedback-vibra/periodic/fallback", + test_fbd_feedback_vibra_periodic_fallback); return g_test_run(); }