1 /*
2  * Copyright (C) 2020 Purism SPC
3  * SPDX-License-Identifier: GPL-3.0+
4  * Author: Guido Günther <agx@sigxcpu.org>
5  */
6 
7 #define G_LOG_DOMAIN "fbd-feedback-vibra-rumble"
8 
9 #include "fbd-enums.h"
10 #include "fbd-feedback-vibra-rumble.h"
11 #include "fbd-feedback-manager.h"
12 
13 /**
14  * SECTION:fbd-feedback-vibra
15  * @short_description: Describes a rumble feedback via a haptic motor
16  * @Title: FbdFeedbackVibraRumble
17  *
18  * The #FbdVibraVibraRumble describes the properties of a haptic feedback
19  * event. It knows nothing about the hardware itself but calls
20  * #FbdDevVibra for that.
21  */
22 
23 enum {
24   PROP_0,
25   PROP_COUNT,
26   PROP_PAUSE,
27   PROP_LAST_PROP,
28 };
29 static GParamSpec *props[PROP_LAST_PROP];
30 
31 typedef struct _FbdFeedbackVibraRumble {
32   FbdFeedbackVibra parent;
33 
34   guint count;   /* number of rumbles */
35   guint pause;   /* pause in msecs */
36 
37   guint rumble;  /* rumble in msecs */
38   guint periods; /* number of periods to play */
39   guint timer_id;
40 } FbdFeedbackVibraRumble;
41 
42 G_DEFINE_TYPE (FbdFeedbackVibraRumble, fbd_feedback_vibra_rumble, FBD_TYPE_FEEDBACK_VIBRA);
43 
44 static void
fbd_feedback_vibra_rumble_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)45 fbd_feedback_vibra_rumble_set_property (GObject      *object,
46 					guint         property_id,
47 					const GValue *value,
48 					GParamSpec   *pspec)
49 {
50   FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (object);
51 
52   switch (property_id) {
53   case PROP_COUNT:
54     self->count = g_value_get_uint (value);
55     break;
56   case PROP_PAUSE:
57     self->pause = g_value_get_uint (value);
58     break;
59   default:
60     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
61     break;
62   }
63 }
64 
65 static void
fbd_feedback_vibra_rumble_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)66 fbd_feedback_vibra_rumble_get_property (GObject  *object,
67 					  guint       property_id,
68 					  GValue     *value,
69 					  GParamSpec *pspec)
70 {
71   FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (object);
72 
73   switch (property_id) {
74   case PROP_COUNT:
75     g_value_set_uint (value, self->count);
76     break;
77   case PROP_PAUSE:
78     g_value_set_uint (value, self->pause);
79     break;
80   default:
81     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82     break;
83   }
84 }
85 
86 static gboolean
on_period_ended(FbdFeedbackVibraRumble * self)87 on_period_ended (FbdFeedbackVibraRumble *self)
88 {
89   FbdFeedbackManager *manager = fbd_feedback_manager_get_default ();
90   FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager);
91 
92   g_return_val_if_fail (FBD_IS_FEEDBACK_VIBRA_RUMBLE (self), G_SOURCE_REMOVE);
93 
94   if (self->periods) {
95     fbd_dev_vibra_rumble (dev, self->rumble, FALSE);
96     self->periods--;
97     return G_SOURCE_CONTINUE;
98   }
99   return G_SOURCE_REMOVE;
100 }
101 
102 static void
fbd_feedback_vibra_rumble_end_vibra(FbdFeedbackVibra * vibra)103 fbd_feedback_vibra_rumble_end_vibra (FbdFeedbackVibra *vibra)
104 {
105   FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (vibra);
106   FbdFeedbackManager *manager = fbd_feedback_manager_get_default ();
107   FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager);
108 
109   fbd_dev_vibra_stop (dev);
110   g_clear_handle_id(&self->timer_id, g_source_remove);
111 }
112 
113 static void
fbd_feedback_vibra_rumble_start_vibra(FbdFeedbackVibra * vibra)114 fbd_feedback_vibra_rumble_start_vibra (FbdFeedbackVibra *vibra)
115 {
116   FbdFeedbackVibraRumble *self = FBD_FEEDBACK_VIBRA_RUMBLE (vibra);
117   FbdFeedbackManager *manager = fbd_feedback_manager_get_default ();
118   FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager);
119   guint duration = fbd_feedback_vibra_get_duration (vibra);
120   guint period;
121 
122   self->rumble = (duration / self->count) - self->pause;
123   if (self->rumble <= 0) {
124     self->rumble = FBD_FEEDBACK_VIBRA_DEFAULT_DURATION;
125     self->pause = 0;
126     self->count = 1;
127   }
128   period = self->rumble + self->pause;
129   self->periods = self->count;
130 
131   g_debug ("Rumble Vibra event: duration %d, rumble: %d, pause: %d, period: %d",
132 	   duration, self->rumble, self->pause, period);
133   fbd_dev_vibra_rumble (dev, self->rumble, TRUE);
134   self->periods--;
135   if (self->periods) {
136     self->timer_id = g_timeout_add (period, (GSourceFunc) on_period_ended, self);
137   }
138 }
139 
140 static gboolean
fbd_feedback_vibra_rumble_is_available(FbdFeedbackBase * base)141 fbd_feedback_vibra_rumble_is_available (FbdFeedbackBase *base)
142 {
143   FbdFeedbackManager *manager = fbd_feedback_manager_get_default ();
144   FbdDevVibra *dev = fbd_feedback_manager_get_dev_vibra (manager);
145 
146   return FBD_IS_DEV_VIBRA (dev);
147 }
148 
149 static void
fbd_feedback_vibra_rumble_class_init(FbdFeedbackVibraRumbleClass * klass)150 fbd_feedback_vibra_rumble_class_init (FbdFeedbackVibraRumbleClass *klass)
151 {
152   GObjectClass *object_class = G_OBJECT_CLASS (klass);
153   FbdFeedbackBaseClass *base_class = FBD_FEEDBACK_BASE_CLASS (klass);
154   FbdFeedbackVibraClass *vibra_class = FBD_FEEDBACK_VIBRA_CLASS (klass);
155 
156   object_class->set_property = fbd_feedback_vibra_rumble_set_property;
157   object_class->get_property = fbd_feedback_vibra_rumble_get_property;
158 
159   base_class->is_available = fbd_feedback_vibra_rumble_is_available;
160 
161   vibra_class->start_vibra = fbd_feedback_vibra_rumble_start_vibra;
162   vibra_class->end_vibra = fbd_feedback_vibra_rumble_end_vibra;
163 
164   props[PROP_COUNT] =
165     g_param_spec_uint (
166       "count",
167       "Count",
168       "The number of rumbles",
169       0, G_MAXINT, 1,
170       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
171 
172   props[PROP_PAUSE] =
173     g_param_spec_uint (
174       "pause",
175       "Pause",
176       "The pause in msecs between rumbles",
177       0, G_MAXINT, 0,
178       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
179 
180   g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
181 }
182 
183 static void
fbd_feedback_vibra_rumble_init(FbdFeedbackVibraRumble * self)184 fbd_feedback_vibra_rumble_init (FbdFeedbackVibraRumble *self)
185 {
186   self->count = 1;
187 }
188