1 /* ide-device-provider.c
2  *
3  * Copyright 2015-2019 Christian Hergert <christian@hergert.me>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  * SPDX-License-Identifier: GPL-3.0-or-later
19  */
20 
21 #define G_LOG_DOMAIN "ide-device-provider"
22 
23 #include "config.h"
24 
25 #include "ide-device.h"
26 #include "ide-device-provider.h"
27 
28 typedef struct
29 {
30   GPtrArray *devices;
31 } IdeDeviceProviderPrivate;
32 
33 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeDeviceProvider, ide_device_provider, IDE_TYPE_OBJECT)
34 
35 enum {
36   DEVICE_ADDED,
37   DEVICE_REMOVED,
38   N_SIGNALS
39 };
40 
41 static guint signals [N_SIGNALS];
42 
43 static void
ide_device_provider_real_load_async(IdeDeviceProvider * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)44 ide_device_provider_real_load_async (IdeDeviceProvider   *self,
45                                      GCancellable        *cancellable,
46                                      GAsyncReadyCallback  callback,
47                                      gpointer             user_data)
48 {
49   g_task_report_new_error (self, callback, user_data,
50                            ide_device_provider_real_load_async,
51                            G_IO_ERROR,
52                            G_IO_ERROR_NOT_SUPPORTED,
53                            "%s does not implement load_async",
54                            G_OBJECT_TYPE_NAME (self));
55 }
56 
57 static gboolean
ide_device_provider_real_load_finish(IdeDeviceProvider * self,GAsyncResult * result,GError ** error)58 ide_device_provider_real_load_finish (IdeDeviceProvider  *self,
59                                       GAsyncResult       *result,
60                                       GError            **error)
61 {
62   return g_task_propagate_boolean (G_TASK (result), error);
63 }
64 
65 static void
ide_device_provider_real_device_added(IdeDeviceProvider * self,IdeDevice * device)66 ide_device_provider_real_device_added (IdeDeviceProvider *self,
67                                        IdeDevice         *device)
68 {
69   IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (self);
70 
71   g_assert (IDE_IS_DEVICE_PROVIDER (self));
72   g_assert (IDE_IS_DEVICE (device));
73 
74   if (priv->devices == NULL)
75     priv->devices = g_ptr_array_new_with_free_func (g_object_unref);
76   g_ptr_array_add (priv->devices, g_object_ref (device));
77 }
78 
79 static void
ide_device_provider_real_device_removed(IdeDeviceProvider * self,IdeDevice * device)80 ide_device_provider_real_device_removed (IdeDeviceProvider *self,
81                                          IdeDevice         *device)
82 {
83   IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (self);
84 
85   g_assert (IDE_IS_DEVICE_PROVIDER (self));
86   g_assert (IDE_IS_DEVICE (device));
87 
88   /* Maybe we just disposed */
89   if (priv->devices == NULL)
90     return;
91 
92   if (!g_ptr_array_remove (priv->devices, device))
93     g_warning ("No such device \"%s\" found in \"%s\"",
94                G_OBJECT_TYPE_NAME (device),
95                G_OBJECT_TYPE_NAME (self));
96 }
97 
98 static void
ide_device_provider_dispose(GObject * object)99 ide_device_provider_dispose (GObject *object)
100 {
101   IdeDeviceProvider *self = (IdeDeviceProvider *)object;
102   IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (self);
103 
104   g_clear_pointer (&priv->devices, g_ptr_array_unref);
105 
106   G_OBJECT_CLASS (ide_device_provider_parent_class)->dispose (object);
107 }
108 
109 static void
ide_device_provider_class_init(IdeDeviceProviderClass * klass)110 ide_device_provider_class_init (IdeDeviceProviderClass *klass)
111 {
112   GObjectClass *object_class = G_OBJECT_CLASS (klass);
113 
114   object_class->dispose = ide_device_provider_dispose;
115 
116   klass->device_added = ide_device_provider_real_device_added;
117   klass->device_removed = ide_device_provider_real_device_removed;
118   klass->load_async = ide_device_provider_real_load_async;
119   klass->load_finish = ide_device_provider_real_load_finish;
120 
121   /**
122    * IdeDeviceProvider::device-added:
123    * @self: an #IdeDeviceProvider
124    * @device: an #IdeDevice
125    *
126    * The "device-added" signal is emitted when a provider has discovered
127    * a device has become available.
128    *
129    * Subclasses of #IdeDeviceManager must chain-up if they override the
130    * #IdeDeviceProviderClass.device_added vfunc.
131    *
132    * Since: 3.32
133    */
134   signals [DEVICE_ADDED] =
135     g_signal_new ("device-added",
136                   G_TYPE_FROM_CLASS (klass),
137                   G_SIGNAL_RUN_LAST,
138                   G_STRUCT_OFFSET (IdeDeviceProviderClass, device_added),
139                   NULL, NULL,
140                   g_cclosure_marshal_VOID__OBJECT,
141                   G_TYPE_NONE, 1, IDE_TYPE_DEVICE);
142   g_signal_set_va_marshaller (signals [DEVICE_ADDED],
143                               G_TYPE_FROM_CLASS (klass),
144                               g_cclosure_marshal_VOID__OBJECTv);
145 
146   /**
147    * IdeDeviceProvider::device-removed:
148    * @self: an #IdeDeviceProvider
149    * @device: an #IdeDevice
150    *
151    * The "device-removed" signal is emitted when a provider has discovered
152    * a device is no longer available.
153    *
154    * Subclasses of #IdeDeviceManager must chain-up if they override the
155    * #IdeDeviceProviderClass.device_removed vfunc.
156    *
157    * Since: 3.32
158    */
159   signals [DEVICE_REMOVED] =
160     g_signal_new ("device-removed",
161                   G_TYPE_FROM_CLASS (klass),
162                   G_SIGNAL_RUN_LAST,
163                   G_STRUCT_OFFSET (IdeDeviceProviderClass, device_removed),
164                   NULL, NULL,
165                   g_cclosure_marshal_VOID__OBJECT,
166                   G_TYPE_NONE, 1, IDE_TYPE_DEVICE);
167   g_signal_set_va_marshaller (signals [DEVICE_REMOVED],
168                               G_TYPE_FROM_CLASS (klass),
169                               g_cclosure_marshal_VOID__OBJECTv);
170 }
171 
172 static void
ide_device_provider_init(IdeDeviceProvider * self)173 ide_device_provider_init (IdeDeviceProvider *self)
174 {
175 }
176 
177 /**
178  * ide_device_provider_emit_device_added:
179  *
180  * Emits the #IdeDeviceProvider::device-added signal.
181  *
182  * This should only be called by subclasses of #IdeDeviceProvider when
183  * a new device has been discovered.
184  *
185  * Since: 3.32
186  */
187 void
ide_device_provider_emit_device_added(IdeDeviceProvider * provider,IdeDevice * device)188 ide_device_provider_emit_device_added (IdeDeviceProvider *provider,
189                                        IdeDevice         *device)
190 {
191   g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
192   g_return_if_fail (IDE_IS_DEVICE (device));
193 
194   g_signal_emit (provider, signals [DEVICE_ADDED], 0, device);
195 }
196 
197 /**
198  * ide_device_provider_emit_device_removed:
199  *
200  * Emits the #IdeDeviceProvider::device-removed signal.
201  *
202  * This should only be called by subclasses of #IdeDeviceProvider when
203  * a previously added device has been removed.
204  *
205  * Since: 3.32
206  */
207 void
ide_device_provider_emit_device_removed(IdeDeviceProvider * provider,IdeDevice * device)208 ide_device_provider_emit_device_removed (IdeDeviceProvider *provider,
209                                          IdeDevice         *device)
210 {
211   g_return_if_fail (IDE_IS_DEVICE_PROVIDER (provider));
212   g_return_if_fail (IDE_IS_DEVICE (device));
213 
214   g_signal_emit (provider, signals [DEVICE_REMOVED], 0, device);
215 }
216 
217 /**
218  * ide_device_provider_load_async:
219  * @self: an #IdeDeviceProvider
220  * @cancellable: (nullable): a #GCancellable or %NULL
221  * @callback: (nullable): a #GAsyncReadyCallback to execute upon completion
222  * @user_data: closure data for @callback
223  *
224  * Requests that the #IdeDeviceProvider asynchronously load any known devices.
225  *
226  * This should only be called once on an #IdeDeviceProvider. It is an error
227  * to call this function more than once for a single #IdeDeviceProvider.
228  *
229  * #IdeDeviceProvider implementations are expected to emit the
230  * #IdeDeviceProvider::device-added signal for each device they've discovered.
231  * That should be done for known devices before returning from the asynchronous
232  * operation so that the device manager does not need to wait for additional
233  * devices to enter the "settled" state.
234  *
235  * Since: 3.32
236  */
237 void
ide_device_provider_load_async(IdeDeviceProvider * self,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)238 ide_device_provider_load_async (IdeDeviceProvider   *self,
239                                 GCancellable        *cancellable,
240                                 GAsyncReadyCallback  callback,
241                                 gpointer             user_data)
242 {
243   g_return_if_fail (IDE_IS_DEVICE_PROVIDER (self));
244   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
245 
246   IDE_DEVICE_PROVIDER_GET_CLASS (self)->load_async (self, cancellable, callback, user_data);
247 }
248 
249 /**
250  * ide_device_provider_load_finish:
251  * @self: an #IdeDeviceProvider
252  * @result: a #GAsyncResult provided to callback
253  * @error: a location for a #GError, or %NULL
254  *
255  * Completes an asynchronous request to load known devices via
256  * ide_device_provider_load_async().
257  *
258  * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
259  *
260  * Since: 3.32
261  */
262 gboolean
ide_device_provider_load_finish(IdeDeviceProvider * self,GAsyncResult * result,GError ** error)263 ide_device_provider_load_finish (IdeDeviceProvider  *self,
264                                  GAsyncResult       *result,
265                                  GError            **error)
266 {
267   g_return_val_if_fail (IDE_IS_DEVICE_PROVIDER (self), FALSE);
268   g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
269 
270   return IDE_DEVICE_PROVIDER_GET_CLASS (self)->load_finish (self, result, error);
271 }
272 
273 /**
274  * ide_device_provider_get_devices:
275  * @self: an #IdeDeviceProvider
276  *
277  * Gets a new #GPtrArray containing a list of #IdeDevice instances that were
278  * registered by the #IdeDeviceProvider
279  *
280  * Returns: (transfer full) (element-type Ide.Device) (not nullable):
281  *   a #GPtrArray of #IdeDevice.
282  *
283  * Since: 3.32
284  */
285 GPtrArray *
ide_device_provider_get_devices(IdeDeviceProvider * self)286 ide_device_provider_get_devices (IdeDeviceProvider *self)
287 {
288   IdeDeviceProviderPrivate *priv = ide_device_provider_get_instance_private (self);
289   g_autoptr(GPtrArray) devices = NULL;
290 
291   g_return_val_if_fail (IDE_IS_DEVICE_PROVIDER (self), NULL);
292 
293   devices = g_ptr_array_new ();
294 
295   if (priv->devices != NULL)
296     {
297       for (guint i = 0; i < priv->devices->len; i++)
298         g_ptr_array_add (devices, g_object_ref (g_ptr_array_index (priv->devices, i)));
299     }
300 
301   return g_steal_pointer (&devices);
302 }
303