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