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-base"
8 
9 #include "fbd-feedback-base.h"
10 
11 /**
12  * SECTION:fbd-feedback-base
13  * @short_description: Base class for different feedback types
14  * @Title: FbdFeedbackManager
15  *
16  * You usually don't want to create objects of this type. It just
17  * serves as a base class for other feedback types.
18  */
19 
20 enum {
21   PROP_0,
22   PROP_EVENT_NAME,
23   PROP_LAST_PROP,
24 };
25 static GParamSpec *props[PROP_LAST_PROP];
26 
27 enum {
28   SIGNAL_ENDED,
29   N_SIGNALS
30 };
31 static guint signals[N_SIGNALS];
32 
33 typedef struct _FbdFeedbackBasePrivate {
34   gchar *event_name;
35   gboolean ended;
36 } FbdFeedbackBasePrivate;
37 
38 G_DEFINE_TYPE_WITH_PRIVATE (FbdFeedbackBase, fbd_feedback_base, G_TYPE_OBJECT);
39 
40 static void
fbd_feedback_base_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)41 fbd_feedback_base_set_property (GObject        *object,
42 				guint         property_id,
43 				const GValue *value,
44 				GParamSpec   *pspec)
45 {
46   FbdFeedbackBase *self = FBD_FEEDBACK_BASE (object);
47   FbdFeedbackBasePrivate *priv = fbd_feedback_base_get_instance_private (self);
48 
49   switch (property_id) {
50   case PROP_EVENT_NAME:
51     g_free (priv->event_name);
52     priv->event_name = g_value_dup_string (value);
53     break;
54   default:
55     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
56     break;
57   }
58 }
59 
60 
61 static void
fbd_feedback_base_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)62 fbd_feedback_base_get_property (GObject  *object,
63 				guint       property_id,
64 				GValue     *value,
65 				GParamSpec *pspec)
66 {
67   FbdFeedbackBase *self = FBD_FEEDBACK_BASE (object);
68   FbdFeedbackBasePrivate *priv = fbd_feedback_base_get_instance_private (self);
69 
70   switch (property_id) {
71   case PROP_EVENT_NAME:
72     g_value_set_string (value, priv->event_name);
73     break;
74   default:
75     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
76     break;
77   }
78 }
79 
80 static void
fbd_feedback_base_dispose(GObject * object)81 fbd_feedback_base_dispose (GObject *object)
82 {
83   FbdFeedbackBase *self = FBD_FEEDBACK_BASE (object);
84 
85   /* end feedback if running */
86   if (!fbd_feedback_get_ended (self))
87     fbd_feedback_end (self);
88 
89   G_OBJECT_CLASS (fbd_feedback_base_parent_class)->dispose (object);
90 }
91 
92 static void
fbd_feedback_base_finalize(GObject * object)93 fbd_feedback_base_finalize (GObject *object)
94 {
95   FbdFeedbackBase *self = FBD_FEEDBACK_BASE (object);
96   FbdFeedbackBasePrivate *priv = fbd_feedback_base_get_instance_private (self);
97 
98   g_clear_pointer (&priv->event_name, g_free);
99 
100   G_OBJECT_CLASS (fbd_feedback_base_parent_class)->finalize (object);
101 }
102 
103 static void
fbd_feedback_base_class_init(FbdFeedbackBaseClass * klass)104 fbd_feedback_base_class_init (FbdFeedbackBaseClass *klass)
105 {
106   GObjectClass *object_class = G_OBJECT_CLASS (klass);
107 
108   object_class->set_property = fbd_feedback_base_set_property;
109   object_class->get_property = fbd_feedback_base_get_property;
110 
111   object_class->dispose = fbd_feedback_base_dispose;
112   object_class->finalize = fbd_feedback_base_finalize;
113 
114   props[PROP_EVENT_NAME] =
115     g_param_spec_string (
116       "event-name",
117       "Event Name",
118       "The event this feedback is associated with",
119       NULL,
120       G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
121 
122   g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
123 
124   /**
125    * FbdFeedbackBase::ended:
126    *
127    * Emitted when the feedback has ended
128    */
129   signals[SIGNAL_ENDED] = g_signal_new ("ended",
130                                         G_TYPE_FROM_CLASS (klass),
131                                         G_SIGNAL_RUN_LAST, 0, NULL, NULL,
132                                         NULL,
133                                         G_TYPE_NONE,
134                                         0);
135 }
136 
137 static void
fbd_feedback_base_init(FbdFeedbackBase * self)138 fbd_feedback_base_init (FbdFeedbackBase *self)
139 {
140 }
141 
142 /**
143  * fbd_feedback_get_event_name:
144  * @self: The feedback
145  *
146  * Returns: the name from the event naming spec this feedback is associated with.
147  */
148 const gchar *
fbd_feedback_get_event_name(FbdFeedbackBase * self)149 fbd_feedback_get_event_name (FbdFeedbackBase *self)
150 {
151   FbdFeedbackBasePrivate *priv;
152 
153   g_return_val_if_fail (FBD_IS_FEEDBACK_BASE (self), NULL);
154   priv = fbd_feedback_base_get_instance_private (self);
155 
156   return priv->event_name;
157 }
158 
159 /**
160  * fbd_feedback_run:
161  * @self: The feedback to run
162  *
163  * Emit the feedback.
164  */
165 void
fbd_feedback_run(FbdFeedbackBase * self)166 fbd_feedback_run (FbdFeedbackBase *self)
167 {
168   FbdFeedbackBaseClass *klass;
169   FbdFeedbackBasePrivate *priv;
170 
171   g_return_if_fail (FBD_IS_FEEDBACK_BASE (self));
172   priv = fbd_feedback_base_get_instance_private (self);
173 
174   priv->ended = FALSE;
175   klass = FBD_FEEDBACK_BASE_GET_CLASS (self);
176   g_return_if_fail (klass->run);
177   klass->run (self);
178 }
179 
180 /**
181  * fbd_feedback_end:
182  * @self: The feedback to end
183  *
184  * End the feedback immediately.
185  */
186 void
fbd_feedback_end(FbdFeedbackBase * self)187 fbd_feedback_end (FbdFeedbackBase *self)
188 {
189   FbdFeedbackBaseClass *klass;
190 
191   g_return_if_fail (FBD_IS_FEEDBACK_BASE (self));
192 
193   klass = FBD_FEEDBACK_BASE_GET_CLASS (self);
194   g_return_if_fail (klass->end);
195   klass->end (self);
196 }
197 
198 
199 /**
200  * fbd_feedback_get_ended:
201  * @self: The feedback
202  *
203  * Whether the feedback is ended.
204  *
205  * Returns: %TRUE if feedback has ended, otherwise %FALSE.
206  */
207 gboolean
fbd_feedback_get_ended(FbdFeedbackBase * self)208 fbd_feedback_get_ended (FbdFeedbackBase *self)
209 {
210   FbdFeedbackBasePrivate *priv = fbd_feedback_base_get_instance_private (self);
211 
212   return priv->ended;
213 }
214 
215 /**
216  * fbd_feedback_base_done:
217  * @self: The feedback
218  *
219  * Invoked by a derived classes to notify that it's done emitting feedback,
220  * e.g. when the vibra motor stopped or a sound finished playing.
221  */
222 void
fbd_feedback_base_done(FbdFeedbackBase * self)223 fbd_feedback_base_done (FbdFeedbackBase *self)
224 {
225   FbdFeedbackBasePrivate *priv = fbd_feedback_base_get_instance_private (self);
226 
227   priv->ended = TRUE;
228   g_signal_emit (self, signals[SIGNAL_ENDED], 0);
229 }
230 
231 /**
232  * fbd_feedback_available:
233  * @self: The feedback
234  *
235  * Whether this feedback type is available at all. This can be %FALSE e.g.
236  * due to missing hardware.
237  *
238  * Returns: %FALSE if the feedback type is not available at all %TRUE if unsure
239  * or available.
240  */
241 gboolean
fbd_feedback_is_available(FbdFeedbackBase * self)242 fbd_feedback_is_available (FbdFeedbackBase *self)
243 {
244   FbdFeedbackBaseClass *klass;
245 
246   g_return_val_if_fail (FBD_IS_FEEDBACK_BASE (self), FALSE);
247 
248   klass = FBD_FEEDBACK_BASE_GET_CLASS (self);
249   if (klass->is_available)
250     return klass->is_available (self);
251   else
252     return TRUE;
253 }
254 
255