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