1 /* ide-device-manager.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-manager"
22 
23 #include "config.h"
24 
25 #include <glib/gi18n.h>
26 #include <libide-plugins.h>
27 #include <libide-threading.h>
28 #include <libpeas/peas.h>
29 
30 #include "ide-build-manager.h"
31 #include "ide-pipeline.h"
32 #include "ide-deploy-strategy.h"
33 #include "ide-device-manager.h"
34 #include "ide-device-private.h"
35 #include "ide-device-provider.h"
36 #include "ide-device.h"
37 #include "ide-foundry-compat.h"
38 #include "ide-local-device.h"
39 
40 struct _IdeDeviceManager
41 {
42   IdeObject parent_instance;
43 
44   /*
45    * The currently selected device. Various subsystems will track this
46    * to update necessary changes for the device type. For example, the
47    * build pipeline will need to adjust things based on the current
48    * device to ensure we are building for the right architecture.
49    */
50   IdeDevice *device;
51 
52   /*
53    * The devices that have been registered by IdeDeviceProvier plugins.
54    * It always has at least one device, the "local" device (IdeLocalDevice).
55    */
56   GPtrArray *devices;
57 
58   /* Providers that are registered in plugins supporting IdeDeviceProvider. */
59   IdeExtensionSetAdapter *providers;
60 
61   /*
62    * Our menu that contains our list of devices for the user to select. This
63    * is "per-IdeContext" so that it is not global to the system (which would
64    * result in duplicates for each workbench opened).
65    */
66   GMenu *menu;
67   GMenu *menu_section;
68 
69   /*
70    * Our progress in a deployment. Simplifies binding to the progress bar
71    * in the omnibar.
72    */
73   gdouble progress;
74 
75   guint loading : 1;
76 };
77 
78 typedef struct
79 {
80   IdeObjectArray   *strategies;
81   IdePipeline *pipeline;
82 } DeployState;
83 
84 typedef struct
85 {
86   gint n_active;
87 } InitState;
88 
89 static void list_model_init_interface        (GListModelInterface *iface);
90 static void async_initable_init_iface        (GAsyncInitableIface *iface);
91 static void ide_device_manager_action_device (IdeDeviceManager    *self,
92                                               GVariant            *param);
93 static void ide_device_manager_action_deploy (IdeDeviceManager    *self,
94                                               GVariant            *param);
95 static void ide_device_manager_deploy_tick   (IdeTask             *task);
96 
97 DZL_DEFINE_ACTION_GROUP (IdeDeviceManager, ide_device_manager, {
98   { "device", ide_device_manager_action_device, "s", "'local'" },
99   { "deploy", ide_device_manager_action_deploy },
100 })
101 
102 G_DEFINE_FINAL_TYPE_WITH_CODE (IdeDeviceManager, ide_device_manager, IDE_TYPE_OBJECT,
103                          G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP,
104                                                 ide_device_manager_init_action_group)
105                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_init_iface)
106                          G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_init_interface))
107 
108 enum {
109   PROP_0,
110   PROP_DEVICE,
111   PROP_PROGRESS,
112   N_PROPS
113 };
114 
115 enum {
116   DEPLOY_STARTED,
117   DEPLOY_FINISHED,
118   N_SIGNALS
119 };
120 
121 static GParamSpec *properties [N_PROPS];
122 static guint signals [N_SIGNALS];
123 
124 static void
deploy_state_free(DeployState * state)125 deploy_state_free (DeployState *state)
126 {
127   g_clear_object (&state->pipeline);
128   g_clear_pointer (&state->strategies, ide_object_array_unref);
129   g_slice_free (DeployState, state);
130 }
131 
132 static void
ide_device_manager_provider_device_added_cb(IdeDeviceManager * self,IdeDevice * device,IdeDeviceProvider * provider)133 ide_device_manager_provider_device_added_cb (IdeDeviceManager  *self,
134                                              IdeDevice         *device,
135                                              IdeDeviceProvider *provider)
136 {
137   g_autoptr(GMenuItem) menu_item = NULL;
138   const gchar *display_name;
139   const gchar *icon_name;
140   const gchar *device_id;
141   guint position;
142 
143   IDE_ENTRY;
144 
145   g_assert (IDE_IS_DEVICE_MANAGER (self));
146   g_assert (IDE_IS_DEVICE (device));
147   g_assert (!provider || IDE_IS_DEVICE_PROVIDER (provider));
148 
149   device_id = ide_device_get_id (device);
150   icon_name = ide_device_get_icon_name (device);
151   display_name = ide_device_get_display_name (device);
152 
153   IDE_TRACE_MSG ("Discovered device %s", device_id);
154 
155   /* Notify user of new device if this is after initial loading */
156   if (!self->loading)
157     {
158       g_autoptr(IdeNotification) notif = NULL;
159       g_autofree gchar *title = NULL;
160 
161       /* translators: %s is replaced with the external device name */
162       title = g_strdup_printf (_("Discovered device “%s”"), display_name);
163       notif = g_object_new (IDE_TYPE_NOTIFICATION,
164                             "id", "org.gnome.builder.device-manager.added",
165                             "title", title,
166                             "icon-name", icon_name,
167                             NULL);
168 
169       ide_notification_attach (notif, IDE_OBJECT (self));
170       ide_notification_withdraw_in_seconds (notif, -1);
171     }
172 
173   /* First add the device to the array, we'll notify observers later */
174   position = self->devices->len;
175   g_ptr_array_add (self->devices, g_object_ref (device));
176 
177   /* Now add a new menu item to our selection model */
178   menu_item = g_menu_item_new (display_name, NULL);
179   g_menu_item_set_attribute (menu_item, "id", "s", device_id);
180   g_menu_item_set_attribute (menu_item, "verb-icon-name", "s", icon_name ?: "computer-symbolic");
181   g_menu_item_set_action_and_target_value (menu_item,
182                                            "device-manager.device",
183                                            g_variant_new_string (device_id));
184   g_menu_append_item (self->menu_section, menu_item);
185 
186   /* Now notify about the new device */
187   g_list_model_items_changed (G_LIST_MODEL (self), position, 0, 1);
188 
189   IDE_EXIT;
190 }
191 
192 static void
ide_device_manager_provider_device_removed_cb(IdeDeviceManager * self,IdeDevice * device,IdeDeviceProvider * provider)193 ide_device_manager_provider_device_removed_cb (IdeDeviceManager  *self,
194                                                IdeDevice         *device,
195                                                IdeDeviceProvider *provider)
196 {
197   const gchar *device_id;
198   GMenu *menu;
199   guint n_items;
200 
201   IDE_ENTRY;
202 
203   g_assert (IDE_IS_DEVICE_MANAGER (self));
204   g_assert (IDE_IS_DEVICE (device));
205   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
206 
207   device_id = ide_device_get_id (device);
208 
209   menu = self->menu_section;
210   n_items = g_menu_model_get_n_items (G_MENU_MODEL (menu));
211 
212   for (guint i = 0; i < n_items; i++)
213     {
214       g_autofree gchar *id = NULL;
215 
216       if (g_menu_model_get_item_attribute (G_MENU_MODEL (menu), i, "id", "s", &id) &&
217           g_strcmp0 (id, device_id) == 0)
218         {
219           g_menu_remove (menu, i);
220           break;
221         }
222     }
223 
224   for (guint i = 0; i < self->devices->len; i++)
225     {
226       IdeDevice *element = g_ptr_array_index (self->devices, i);
227 
228       if (element == device)
229         {
230           g_ptr_array_remove_index (self->devices, i);
231           g_list_model_items_changed (G_LIST_MODEL (self), i, 1, 0);
232           break;
233         }
234     }
235 
236   IDE_EXIT;
237 }
238 
239 static void
ide_device_manager_provider_load_cb(GObject * object,GAsyncResult * result,gpointer user_data)240 ide_device_manager_provider_load_cb (GObject      *object,
241                                      GAsyncResult *result,
242                                      gpointer      user_data)
243 {
244   IdeDeviceProvider *provider = (IdeDeviceProvider *)object;
245   g_autoptr(IdeDeviceManager) self = user_data;
246   g_autoptr(GError) error = NULL;
247 
248   IDE_ENTRY;
249 
250   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
251   g_assert (G_IS_ASYNC_RESULT (result));
252   g_assert (IDE_IS_DEVICE_MANAGER (self));
253 
254   if (!ide_device_provider_load_finish (provider, result, &error))
255     g_warning ("%s failed to load: %s",
256                G_OBJECT_TYPE_NAME (provider),
257                error->message);
258 
259   IDE_EXIT;
260 }
261 
262 static void
ide_device_manager_provider_added_cb(IdeExtensionSetAdapter * set,PeasPluginInfo * plugin_info,PeasExtension * exten,gpointer user_data)263 ide_device_manager_provider_added_cb (IdeExtensionSetAdapter *set,
264                                       PeasPluginInfo         *plugin_info,
265                                       PeasExtension          *exten,
266                                       gpointer                user_data)
267 {
268   IdeDeviceManager *self = user_data;
269   IdeDeviceProvider *provider = (IdeDeviceProvider *)exten;
270   g_autoptr(GPtrArray) devices = NULL;
271 
272   IDE_ENTRY;
273 
274   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
275   g_assert (IDE_IS_DEVICE_MANAGER (self));
276   g_assert (plugin_info != NULL);
277   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
278 
279   ide_object_append (IDE_OBJECT (self), IDE_OBJECT (provider));
280 
281   g_signal_connect_object (provider,
282                            "device-added",
283                            G_CALLBACK (ide_device_manager_provider_device_added_cb),
284                            self,
285                            G_CONNECT_SWAPPED);
286 
287   g_signal_connect_object (provider,
288                            "device-removed",
289                            G_CALLBACK (ide_device_manager_provider_device_removed_cb),
290                            self,
291                            G_CONNECT_SWAPPED);
292 
293   devices = ide_device_provider_get_devices (provider);
294   IDE_PTR_ARRAY_SET_FREE_FUNC (devices, g_object_unref);
295 
296   for (guint i = 0; i < devices->len; i++)
297     {
298       IdeDevice *device = g_ptr_array_index (devices, i);
299 
300       g_assert (IDE_IS_DEVICE (device));
301 
302       ide_device_manager_provider_device_added_cb (self, device, provider);
303     }
304 
305   ide_device_provider_load_async (provider,
306                                   NULL,
307                                   ide_device_manager_provider_load_cb,
308                                   g_object_ref (self));
309 
310   IDE_EXIT;
311 }
312 
313 static void
ide_device_manager_provider_removed_cb(IdeExtensionSetAdapter * set,PeasPluginInfo * plugin_info,PeasExtension * exten,gpointer user_data)314 ide_device_manager_provider_removed_cb (IdeExtensionSetAdapter *set,
315                                         PeasPluginInfo         *plugin_info,
316                                         PeasExtension          *exten,
317                                         gpointer                user_data)
318 {
319   IdeDeviceManager *self = user_data;
320   IdeDeviceProvider *provider = (IdeDeviceProvider *)exten;
321   g_autoptr(GPtrArray) devices = NULL;
322 
323   IDE_ENTRY;
324 
325   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
326   g_assert (IDE_IS_DEVICE_MANAGER (self));
327   g_assert (plugin_info != NULL);
328   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
329 
330   devices = ide_device_provider_get_devices (provider);
331   IDE_PTR_ARRAY_SET_FREE_FUNC (devices, g_object_unref);
332 
333   for (guint i = 0; i < devices->len; i++)
334     {
335       IdeDevice *removed_device = g_ptr_array_index (devices, i);
336 
337       for (guint j = 0; j < self->devices->len; j++)
338         {
339           IdeDevice *device = g_ptr_array_index (self->devices, j);
340 
341           if (device == removed_device)
342             {
343               g_ptr_array_remove_index (self->devices, j);
344               g_list_model_items_changed (G_LIST_MODEL (self), j, 1, 0);
345               break;
346             }
347         }
348     }
349 
350   g_signal_handlers_disconnect_by_func (provider,
351                                         G_CALLBACK (ide_device_manager_provider_device_added_cb),
352                                         self);
353 
354   g_signal_handlers_disconnect_by_func (provider,
355                                         G_CALLBACK (ide_device_manager_provider_device_removed_cb),
356                                         self);
357 
358   ide_object_destroy (IDE_OBJECT (provider));
359 
360   IDE_EXIT;
361 }
362 
363 static void
ide_device_manager_add_local(IdeDeviceManager * self)364 ide_device_manager_add_local (IdeDeviceManager *self)
365 {
366   g_autoptr(IdeDevice) device = NULL;
367   g_autoptr(IdeTriplet) triplet = NULL;
368   g_autofree gchar *arch = NULL;
369 
370   g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
371 
372   triplet = ide_triplet_new_from_system ();
373   device = g_object_new (IDE_TYPE_LOCAL_DEVICE,
374                          "triplet", triplet,
375                          NULL);
376   ide_object_append (IDE_OBJECT (self), IDE_OBJECT (device));
377   ide_device_manager_provider_device_added_cb (self, device, NULL);
378 }
379 
380 static GType
ide_device_manager_get_item_type(GListModel * list_model)381 ide_device_manager_get_item_type (GListModel *list_model)
382 {
383   return IDE_TYPE_DEVICE;
384 }
385 
386 static guint
ide_device_manager_get_n_items(GListModel * list_model)387 ide_device_manager_get_n_items (GListModel *list_model)
388 {
389   IdeDeviceManager *self = (IdeDeviceManager *)list_model;
390 
391   g_assert (IDE_IS_DEVICE_MANAGER (self));
392 
393   return self->devices->len;
394 }
395 
396 static gpointer
ide_device_manager_get_item(GListModel * list_model,guint position)397 ide_device_manager_get_item (GListModel *list_model,
398                              guint       position)
399 {
400   IdeDeviceManager *self = (IdeDeviceManager *)list_model;
401 
402   g_assert (IDE_IS_DEVICE_MANAGER (self));
403   g_assert (position < self->devices->len);
404 
405   return g_object_ref (g_ptr_array_index (self->devices, position));
406 }
407 
408 static void
ide_device_manager_destroy(IdeObject * object)409 ide_device_manager_destroy (IdeObject *object)
410 {
411   IdeDeviceManager *self = (IdeDeviceManager *)object;
412 
413   g_assert (IDE_IS_OBJECT (object));
414   g_assert (IDE_IS_MAIN_THREAD ());
415 
416   ide_clear_and_destroy_object (&self->providers);
417 
418   IDE_OBJECT_CLASS (ide_device_manager_parent_class)->destroy (object);
419 
420   if (self->devices->len > 0)
421     g_ptr_array_remove_range (self->devices, 0, self->devices->len);
422 
423   g_clear_object (&self->device);
424 }
425 
426 static void
ide_device_manager_finalize(GObject * object)427 ide_device_manager_finalize (GObject *object)
428 {
429   IdeDeviceManager *self = (IdeDeviceManager *)object;
430 
431   g_clear_pointer (&self->devices, g_ptr_array_unref);
432   g_clear_object (&self->menu);
433   g_clear_object (&self->menu_section);
434 
435   G_OBJECT_CLASS (ide_device_manager_parent_class)->finalize (object);
436 }
437 
438 static void
ide_device_manager_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)439 ide_device_manager_get_property (GObject    *object,
440                                  guint       prop_id,
441                                  GValue     *value,
442                                  GParamSpec *pspec)
443 {
444   IdeDeviceManager *self = IDE_DEVICE_MANAGER (object);
445 
446   switch (prop_id)
447     {
448     case PROP_DEVICE:
449       g_value_set_object (value, ide_device_manager_get_device (self));
450       break;
451 
452     case PROP_PROGRESS:
453       g_value_set_double (value, ide_device_manager_get_progress (self));
454       break;
455 
456     default:
457       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
458     }
459 }
460 
461 static void
ide_device_manager_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)462 ide_device_manager_set_property (GObject      *object,
463                                  guint         prop_id,
464                                  const GValue *value,
465                                  GParamSpec   *pspec)
466 {
467   IdeDeviceManager *self = IDE_DEVICE_MANAGER (object);
468 
469   switch (prop_id)
470     {
471     case PROP_DEVICE:
472       ide_device_manager_set_device (self, g_value_get_object (value));
473       break;
474 
475     default:
476       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
477     }
478 }
479 
480 static void
ide_device_manager_class_init(IdeDeviceManagerClass * klass)481 ide_device_manager_class_init (IdeDeviceManagerClass *klass)
482 {
483   GObjectClass *object_class = G_OBJECT_CLASS (klass);
484   IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
485 
486   object_class->finalize = ide_device_manager_finalize;
487   object_class->get_property = ide_device_manager_get_property;
488   object_class->set_property = ide_device_manager_set_property;
489 
490   i_object_class->destroy = ide_device_manager_destroy;
491 
492   /**
493    * IdeDeviceManager:device:
494    *
495    * The "device" property indicates the currently selected device by the
496    * user. This is the device we will try to deploy to when running, and
497    * execute the application on.
498    *
499    * Since: 3.32
500    */
501   properties [PROP_DEVICE] =
502     g_param_spec_object ("device",
503                          "Device",
504                          "The currently selected device to build for",
505                          IDE_TYPE_DEVICE,
506                          G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
507 
508   /**
509    * IdeDeviceManager:progress:
510    *
511    * The "progress" property is updated with a value between 0.0 and 1.0 while
512    * the deployment is in progress.
513    *
514    * Since: 3.32
515    */
516   properties [PROP_PROGRESS] =
517     g_param_spec_double ("progress",
518                          "Progress",
519                          "Deployment progress",
520                          0.0, 1.0, 0.0,
521                          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
522 
523   g_object_class_install_properties (object_class, N_PROPS, properties);
524 
525   signals [DEPLOY_STARTED] =
526     g_signal_new ("deploy-started",
527                   G_TYPE_FROM_CLASS (klass),
528                   G_SIGNAL_RUN_LAST,
529                   0, NULL, NULL, NULL, G_TYPE_NONE, 0);
530 
531   signals [DEPLOY_FINISHED] =
532     g_signal_new ("deploy-finished",
533                   G_TYPE_FROM_CLASS (klass),
534                   G_SIGNAL_RUN_LAST,
535                   0, NULL, NULL, NULL, G_TYPE_NONE, 0);
536 }
537 
538 static void
list_model_init_interface(GListModelInterface * iface)539 list_model_init_interface (GListModelInterface *iface)
540 {
541   iface->get_item_type = ide_device_manager_get_item_type;
542   iface->get_n_items = ide_device_manager_get_n_items;
543   iface->get_item = ide_device_manager_get_item;
544 }
545 
546 static void
ide_device_manager_init(IdeDeviceManager * self)547 ide_device_manager_init (IdeDeviceManager *self)
548 {
549   self->devices = g_ptr_array_new_with_free_func (g_object_unref);
550 
551   self->menu = g_menu_new ();
552   self->menu_section = g_menu_new ();
553   g_menu_append_section (self->menu, _("Devices"), G_MENU_MODEL (self->menu_section));
554 }
555 
556 /**
557  * ide_device_manager_get_device_by_id:
558  * @self: an #IdeDeviceManager
559  * @device_id: The device identifier string.
560  *
561  * Fetches the first device that matches the device identifier @device_id.
562  *
563  * Returns: (transfer none): An #IdeDevice or %NULL.
564  *
565  * Since: 3.32
566  */
567 IdeDevice *
ide_device_manager_get_device_by_id(IdeDeviceManager * self,const gchar * device_id)568 ide_device_manager_get_device_by_id (IdeDeviceManager *self,
569                                      const gchar      *device_id)
570 {
571   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), NULL);
572 
573   for (guint i = 0; i < self->devices->len; i++)
574     {
575       IdeDevice *device;
576       const gchar *id;
577 
578       device = g_ptr_array_index (self->devices, i);
579       id = ide_device_get_id (device);
580 
581       if (0 == g_strcmp0 (id, device_id))
582         return device;
583     }
584 
585   return NULL;
586 }
587 
588 /**
589  * ide_device_manager_get_device:
590  * @self: a #IdeDeviceManager
591  *
592  * Gets the currently selected device.
593  * Usually, this is an #IdeLocalDevice.
594  *
595  * Returns: (transfer none) (not nullable): an #IdeDevice
596  *
597  * Since: 3.32
598  */
599 IdeDevice *
ide_device_manager_get_device(IdeDeviceManager * self)600 ide_device_manager_get_device (IdeDeviceManager *self)
601 {
602   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), NULL);
603   g_return_val_if_fail (self->devices->len > 0, NULL);
604 
605   if (self->device == NULL)
606     {
607       for (guint i = 0; i < self->devices->len; i++)
608         {
609           IdeDevice *device = g_ptr_array_index (self->devices, i);
610 
611           if (IDE_IS_LOCAL_DEVICE (device))
612             return device;
613         }
614 
615       g_assert_not_reached ();
616     }
617 
618   return self->device;
619 }
620 
621 /**
622  * ide_device_manager_set_device:
623  * @self: an #IdeDeviceManager
624  * @device: (nullable): an #IdeDevice or %NULL
625  *
626  * Sets the #IdeDeviceManager:device property, which is the currently selected
627  * device. Builder uses this to determine how to build the current project for
628  * the devices architecture and operating system.
629  *
630  * If @device is %NULL, the local device will be used.
631  *
632  * Since: 3.32
633  */
634 void
ide_device_manager_set_device(IdeDeviceManager * self,IdeDevice * device)635 ide_device_manager_set_device (IdeDeviceManager *self,
636                                IdeDevice        *device)
637 {
638   IDE_ENTRY;
639 
640   g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
641   g_return_if_fail (!device || IDE_IS_DEVICE (device));
642 
643   /* Short-circuit if we're setting to local and the current
644    * device is already local (null).
645    */
646   if (self->device == NULL &&
647       device != NULL &&
648       ide_str_equal0 ("local", ide_device_get_id (device)))
649     return;
650 
651   if (g_set_object (&self->device, device))
652     {
653       const gchar *device_id = NULL;
654       IdeBuildManager *build_manager;
655       IdeContext *context;
656 
657       context = ide_object_get_context (IDE_OBJECT (self));
658       build_manager = ide_build_manager_from_context (context);
659 
660       if (device != NULL)
661         device_id = ide_device_get_id (device);
662 
663       if (device_id == NULL)
664         device_id = "local";
665 
666       IDE_TRACE_MSG ("Device set to %s", device_id);
667 
668       ide_device_manager_set_action_state (self, "device", g_variant_new_string (device_id));
669       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_DEVICE]);
670 
671       ide_build_manager_invalidate (build_manager);
672     }
673 
674   IDE_EXIT;
675 }
676 
677 static void
ide_device_manager_action_device(IdeDeviceManager * self,GVariant * param)678 ide_device_manager_action_device (IdeDeviceManager *self,
679                                   GVariant         *param)
680 {
681   const gchar *device_id;
682   IdeDevice *device;
683 
684   IDE_ENTRY;
685 
686   g_assert (IDE_IS_DEVICE_MANAGER (self));
687   g_assert (param != NULL);
688   g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_STRING));
689 
690   if (!(device_id = g_variant_get_string (param, NULL)))
691     device_id = "local";
692 
693   IDE_TRACE_MSG ("Setting device to \"%s\"", device_id);
694 
695   if (!(device = ide_device_manager_get_device_by_id (self, device_id)))
696     {
697       g_debug ("No such device \"%s\"", device_id);
698       IDE_EXIT;
699     }
700 
701   ide_device_manager_set_device (self, device);
702 }
703 
704 static void
log_deploy_error(GObject * object,GAsyncResult * result,gpointer user_data)705 log_deploy_error (GObject      *object,
706                   GAsyncResult *result,
707                   gpointer      user_data)
708 {
709   g_autoptr(GError) error = NULL;
710 
711   g_assert (IDE_IS_DEVICE_MANAGER (object));
712   g_assert (G_IS_ASYNC_RESULT (result));
713 
714   if (!ide_device_manager_deploy_finish (IDE_DEVICE_MANAGER (object), result, &error))
715     ide_object_warning (object, "%s", error->message);
716 }
717 
718 static void
ide_device_manager_action_deploy(IdeDeviceManager * self,GVariant * param)719 ide_device_manager_action_deploy (IdeDeviceManager *self,
720                                   GVariant         *param)
721 {
722   IdePipeline *pipeline;
723   IdeBuildManager *build_manager;
724   IdeContext *context;
725 
726   g_assert (IDE_IS_DEVICE_MANAGER (self));
727 
728   context = ide_object_get_context (IDE_OBJECT (self));
729   build_manager = ide_build_manager_from_context (context);
730   pipeline = ide_build_manager_get_pipeline (build_manager);
731 
732   if (!ide_pipeline_is_ready (pipeline))
733     ide_context_warning (context, _("Cannot deploy to device, build pipeline is not initialized"));
734   else
735     ide_device_manager_deploy_async (self, pipeline, NULL, log_deploy_error, NULL);
736 }
737 
738 static void
deploy_progress_cb(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)739 deploy_progress_cb (goffset  current_num_bytes,
740                     goffset  total_num_bytes,
741                     gpointer user_data)
742 {
743   IdeDeviceManager *self = user_data;
744   gdouble progress = 0.0;
745 
746   g_assert (IDE_IS_DEVICE_MANAGER (self));
747 
748   if (total_num_bytes > 0)
749     progress = current_num_bytes / total_num_bytes;
750 
751   self->progress = CLAMP (progress, 0.0, 1.0);
752   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
753 }
754 
755 static void
collect_strategies(PeasExtensionSet * set,PeasPluginInfo * plugin_info,PeasExtension * exten,gpointer user_data)756 collect_strategies (PeasExtensionSet *set,
757                     PeasPluginInfo   *plugin_info,
758                     PeasExtension    *exten,
759                     gpointer          user_data)
760 {
761   IdeObjectArray *strategies = user_data;
762   IdeDeployStrategy *strategy = (IdeDeployStrategy *)exten;
763 
764   g_assert (PEAS_IS_EXTENSION_SET (set));
765   g_assert (plugin_info != NULL);
766   g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
767   g_assert (strategies != NULL);
768 
769   ide_object_array_add (strategies, strategy);
770 }
771 
772 static void
ide_device_manager_deploy_cb(GObject * object,GAsyncResult * result,gpointer user_data)773 ide_device_manager_deploy_cb (GObject      *object,
774                               GAsyncResult *result,
775                               gpointer      user_data)
776 {
777   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
778   g_autoptr(GError) error = NULL;
779   g_autoptr(IdeTask) task = user_data;
780 
781   IDE_ENTRY;
782 
783   g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
784   g_assert (G_IS_ASYNC_RESULT (result));
785   g_assert (IDE_IS_TASK (task));
786 
787   if (!ide_deploy_strategy_deploy_finish (strategy, result, &error))
788     ide_task_return_error (task, g_steal_pointer (&error));
789   else
790     ide_task_return_boolean (task, TRUE);
791 
792   IDE_EXIT;
793 }
794 
795 static void
ide_device_manager_deploy_load_cb(GObject * object,GAsyncResult * result,gpointer user_data)796 ide_device_manager_deploy_load_cb (GObject      *object,
797                                    GAsyncResult *result,
798                                    gpointer      user_data)
799 {
800   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
801   g_autoptr(GError) error = NULL;
802   g_autoptr(IdeTask) task = user_data;
803   IdeDeviceManager *self;
804   DeployState *state;
805 
806   IDE_ENTRY;
807 
808   g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
809   g_assert (G_IS_ASYNC_RESULT (result));
810   g_assert (IDE_IS_TASK (task));
811 
812   if (!ide_deploy_strategy_load_finish (strategy, result, &error))
813     {
814       g_debug ("Deploy strategy failed to load: %s", error->message);
815       ide_object_destroy (IDE_OBJECT (strategy));
816       ide_device_manager_deploy_tick (task);
817       IDE_EXIT;
818     }
819 
820   /* Okay, we found a match. Now deploy to the device. */
821 
822   self = ide_task_get_source_object (task);
823   state = ide_task_get_task_data (task);
824 
825   g_assert (IDE_IS_DEVICE_MANAGER (self));
826   g_assert (state != NULL);
827   g_assert (state->strategies != NULL);
828   g_assert (IDE_IS_PIPELINE (state->pipeline));
829 
830   ide_deploy_strategy_deploy_async (strategy,
831                                     state->pipeline,
832                                     deploy_progress_cb,
833                                     g_object_ref (self),
834                                     g_object_unref,
835                                     ide_task_get_cancellable (task),
836                                     ide_device_manager_deploy_cb,
837                                     g_object_ref (task));
838 
839   IDE_EXIT;
840 }
841 
842 static void
ide_device_manager_deploy_tick(IdeTask * task)843 ide_device_manager_deploy_tick (IdeTask *task)
844 {
845   g_autoptr(IdeDeployStrategy) strategy = NULL;
846   DeployState *state;
847 
848   IDE_ENTRY;
849 
850   g_assert (IDE_IS_TASK (task));
851 
852   state = ide_task_get_task_data (task);
853 
854   g_assert (state != NULL);
855   g_assert (state->strategies != NULL);
856   g_assert (IDE_IS_PIPELINE (state->pipeline));
857 
858   if (state->strategies->len == 0)
859     {
860       ide_task_return_new_error (task,
861                                  G_IO_ERROR,
862                                  G_IO_ERROR_NOT_SUPPORTED,
863                                  "Failed to locate deployment strategy for device");
864       IDE_EXIT;
865     }
866 
867   strategy = ide_object_array_steal_index (state->strategies, 0);
868 
869   ide_deploy_strategy_load_async (strategy,
870                                   state->pipeline,
871                                   ide_task_get_cancellable (task),
872                                   ide_device_manager_deploy_load_cb,
873                                   g_object_ref (task));
874 
875   IDE_EXIT;
876 }
877 
878 static void
ide_device_manager_deploy_completed(IdeDeviceManager * self,GParamSpec * pspec,IdeTask * task)879 ide_device_manager_deploy_completed (IdeDeviceManager *self,
880                                      GParamSpec       *pspec,
881                                      IdeTask          *task)
882 {
883   g_assert (IDE_IS_DEVICE_MANAGER (self));
884   g_assert (IDE_IS_TASK (task));
885 
886   if (self->progress < 1.0)
887     {
888       self->progress = 1.0;
889       g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
890     }
891 
892   g_signal_emit (self, signals [DEPLOY_FINISHED], 0);
893 }
894 
895 /**
896  * ide_device_manager_deploy_async:
897  * @self: a #IdeDeviceManager
898  * @pipeline: an #IdePipeline
899  * @cancellable: a #GCancellable, or %NULL
900  * @callback: a #GAsyncReadyCallback
901  * @user_data: closure data for @callback
902  *
903  * Requests that the application be deployed to the device. This may need to
904  * be done before running the application so that the device has the most
905  * up to date build.
906  *
907  * Since: 3.32
908  */
909 void
ide_device_manager_deploy_async(IdeDeviceManager * self,IdePipeline * pipeline,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)910 ide_device_manager_deploy_async (IdeDeviceManager    *self,
911                                  IdePipeline    *pipeline,
912                                  GCancellable        *cancellable,
913                                  GAsyncReadyCallback  callback,
914                                  gpointer             user_data)
915 {
916   g_autoptr(PeasExtensionSet) set = NULL;
917   g_autoptr(IdeTask) task = NULL;
918   DeployState *state;
919   IdeDevice *device;
920 
921   IDE_ENTRY;
922 
923   g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
924   g_return_if_fail (IDE_IS_PIPELINE (pipeline));
925   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
926 
927   self->progress = 0.0;
928   g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROGRESS]);
929 
930   g_signal_emit (self, signals [DEPLOY_STARTED], 0);
931 
932   task = ide_task_new (self, cancellable, callback, user_data);
933   ide_task_set_source_tag (task, ide_device_manager_deploy_async);
934 
935   g_signal_connect_object (task,
936                            "notify::completed",
937                            G_CALLBACK (ide_device_manager_deploy_completed),
938                            self,
939                            G_CONNECT_SWAPPED);
940 
941   if (!(device = ide_pipeline_get_device (pipeline)))
942     {
943       ide_task_return_new_error (task,
944                                  G_IO_ERROR,
945                                  G_IO_ERROR_FAILED,
946                                  "Missing device in pipeline");
947       IDE_EXIT;
948     }
949 
950   if (IDE_IS_LOCAL_DEVICE (device))
951     {
952       ide_task_return_boolean (task, TRUE);
953       IDE_EXIT;
954     }
955 
956   state = g_slice_new0 (DeployState);
957   state->pipeline = g_object_ref (pipeline);
958   state->strategies = ide_object_array_new ();
959   ide_task_set_task_data (task, state, deploy_state_free);
960 
961   set = peas_extension_set_new (peas_engine_get_default (),
962                                 IDE_TYPE_DEPLOY_STRATEGY,
963                                 NULL);
964   peas_extension_set_foreach (set, collect_strategies, state->strategies);
965 
966   /* Root the addins as children of us so that they get context access */
967   for (guint i = 0; i < state->strategies->len; i++)
968     ide_object_append (IDE_OBJECT (self),
969                        ide_object_array_index (state->strategies, i));
970 
971   ide_device_manager_deploy_tick (task);
972 
973   IDE_EXIT;
974 }
975 
976 /**
977  * ide_device_manager_deploy_finish:
978  * @self: a #IdeDeviceManager
979  * @result: a #GAsyncResult provided to callback
980  * @error: a location for a #GError, or %NULL
981  *
982  * Completes a request to deploy the application to the device.
983  *
984  * Returns: %TRUE if successful; otherwise %FALSE and @error is set
985  *
986  * Since: 3.32
987  */
988 gboolean
ide_device_manager_deploy_finish(IdeDeviceManager * self,GAsyncResult * result,GError ** error)989 ide_device_manager_deploy_finish (IdeDeviceManager  *self,
990                                   GAsyncResult      *result,
991                                   GError           **error)
992 {
993   gboolean ret;
994 
995   IDE_ENTRY;
996 
997   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), FALSE);
998   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
999   g_return_val_if_fail (ide_task_is_valid (IDE_TASK (result), self), FALSE);
1000 
1001   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
1002 
1003   IDE_RETURN (ret);
1004 }
1005 
1006 static void
ide_device_manager_create_runner_cb(GObject * object,GAsyncResult * result,gpointer user_data)1007 ide_device_manager_create_runner_cb (GObject      *object,
1008                                      GAsyncResult *result,
1009                                      gpointer      user_data)
1010 {
1011   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
1012   g_autoptr(GError) error = NULL;
1013   g_autoptr(IdeTask) task = user_data;
1014   IdeRunner *runner;
1015 
1016   IDE_ENTRY;
1017 
1018   g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
1019   g_assert (G_IS_ASYNC_RESULT (result));
1020   g_assert (IDE_IS_TASK (task));
1021 
1022   runner = ide_deploy_strategy_create_runner_finish (strategy, result, &error);
1023 
1024   if (error)
1025     ide_task_return_error (task, g_steal_pointer (&error));
1026   else
1027     ide_task_return_pointer (task, runner, g_object_unref);
1028 
1029   ide_object_destroy (IDE_OBJECT (strategy));
1030 
1031   IDE_EXIT;
1032 }
1033 
1034 static void
ide_device_manager_create_runner_load_cb(GObject * object,GAsyncResult * result,gpointer user_data)1035 ide_device_manager_create_runner_load_cb (GObject      *object,
1036                                           GAsyncResult *result,
1037                                           gpointer      user_data)
1038 {
1039   IdeDeployStrategy *strategy = (IdeDeployStrategy *)object;
1040   g_autoptr(GError) error = NULL;
1041   g_autoptr(IdeTask) task = user_data;
1042   IdeDeviceManager *self;
1043   DeployState *state;
1044 
1045   IDE_ENTRY;
1046 
1047   g_assert (IDE_IS_DEPLOY_STRATEGY (strategy));
1048   g_assert (G_IS_ASYNC_RESULT (result));
1049   g_assert (IDE_IS_TASK (task));
1050 
1051   if (!ide_deploy_strategy_load_finish (strategy, result, &error))
1052     {
1053       g_debug ("Deploy strategy failed to load: %s", error->message);
1054       ide_object_destroy (IDE_OBJECT (strategy));
1055       ide_device_manager_deploy_tick (task);
1056       IDE_EXIT;
1057     }
1058 
1059   /* Okay, we found a match. Now run on the device. */
1060 
1061   self = ide_task_get_source_object (task);
1062   state = ide_task_get_task_data (task);
1063 
1064   g_assert (IDE_IS_DEVICE_MANAGER (self));
1065   g_assert (state != NULL);
1066   g_assert (state->strategies != NULL);
1067   g_assert (IDE_IS_PIPELINE (state->pipeline));
1068 
1069   ide_deploy_strategy_create_runner_async (strategy,
1070                                            state->pipeline,
1071                                            ide_task_get_cancellable (task),
1072                                            ide_device_manager_create_runner_cb,
1073                                            g_object_ref (task));
1074 
1075   IDE_EXIT;
1076 }
1077 
1078 static void
ide_device_manager_create_runner_tick(IdeTask * task)1079 ide_device_manager_create_runner_tick (IdeTask *task)
1080 {
1081   g_autoptr(IdeDeployStrategy) strategy = NULL;
1082   DeployState *state;
1083 
1084   IDE_ENTRY;
1085 
1086   g_assert (IDE_IS_TASK (task));
1087 
1088   state = ide_task_get_task_data (task);
1089 
1090   g_assert (state != NULL);
1091   g_assert (state->strategies != NULL);
1092   g_assert (IDE_IS_PIPELINE (state->pipeline));
1093 
1094   if (state->strategies->len == 0)
1095     {
1096       ide_task_return_new_error (task,
1097                                  G_IO_ERROR,
1098                                  G_IO_ERROR_NOT_SUPPORTED,
1099                                  "Failed to locate deployment strategy for device");
1100       IDE_EXIT;
1101     }
1102 
1103   strategy = ide_object_array_steal_index (state->strategies, 0);
1104 
1105   ide_deploy_strategy_load_async (strategy,
1106                                   state->pipeline,
1107                                   ide_task_get_cancellable (task),
1108                                   ide_device_manager_create_runner_load_cb,
1109                                   g_object_ref (task));
1110 
1111   IDE_EXIT;
1112 }
1113 
1114 /**
1115  * ide_device_manager_create_runner_async:
1116  * @self: a #IdeDeviceManager
1117  * @pipeline: an #IdePipeline
1118  * @cancellable: a #GCancellable, or %NULL
1119  * @callback: a #GAsyncReadyCallback
1120  * @user_data: closure data for @callback
1121  *
1122  * Requests an #IdeRunner that runs on the current device, if a runner
1123  * other than the default is required.
1124  *
1125  * Since: 41
1126  */
1127 void
ide_device_manager_create_runner_async(IdeDeviceManager * self,IdePipeline * pipeline,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1128 ide_device_manager_create_runner_async (IdeDeviceManager    *self,
1129                                         IdePipeline         *pipeline,
1130                                         GCancellable        *cancellable,
1131                                         GAsyncReadyCallback  callback,
1132                                         gpointer             user_data)
1133 {
1134   g_autoptr(PeasExtensionSet) set = NULL;
1135   g_autoptr(IdeTask) task = NULL;
1136   DeployState *state;
1137   IdeDevice *device;
1138 
1139   IDE_ENTRY;
1140 
1141   g_return_if_fail (IDE_IS_DEVICE_MANAGER (self));
1142   g_return_if_fail (IDE_IS_PIPELINE (pipeline));
1143   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
1144 
1145   task = ide_task_new (self, cancellable, callback, user_data);
1146   ide_task_set_source_tag (task, ide_device_manager_create_runner_async);
1147 
1148   if (!(device = ide_pipeline_get_device (pipeline)))
1149     {
1150       ide_task_return_new_error (task,
1151                                  G_IO_ERROR,
1152                                  G_IO_ERROR_FAILED,
1153                                  "Missing device in pipeline");
1154       IDE_EXIT;
1155     }
1156 
1157   if (IDE_IS_LOCAL_DEVICE (device))
1158     {
1159       (ide_task_return_pointer) (task, NULL, NULL);
1160       IDE_EXIT;
1161     }
1162 
1163   state = g_slice_new0 (DeployState);
1164   state->pipeline = g_object_ref (pipeline);
1165   state->strategies = ide_object_array_new ();
1166   ide_task_set_task_data (task, state, deploy_state_free);
1167 
1168   set = peas_extension_set_new (peas_engine_get_default (),
1169                                 IDE_TYPE_DEPLOY_STRATEGY,
1170                                 NULL);
1171   peas_extension_set_foreach (set, collect_strategies, state->strategies);
1172 
1173   /* Root the addins as children of us so that they get context access */
1174   for (guint i = 0; i < state->strategies->len; i++)
1175     ide_object_append (IDE_OBJECT (self),
1176                        ide_object_array_index (state->strategies, i));
1177 
1178   ide_device_manager_create_runner_tick (task);
1179 
1180   IDE_EXIT;
1181 }
1182 
1183 /**
1184  * ide_device_manager_create_runner_finish:
1185  * @self: a #IdeDeviceManager
1186  * @result: a #GAsyncResult provided to callback
1187  * @error: a location for a #GError, or %NULL
1188  *
1189  * Completes a request to create an #IdeRunner to run on the device.
1190  *
1191  * Returns: (transfer full): An #IdeRunner or %NULL.
1192  *
1193  * Since: 41
1194  */
1195 IdeRunner *
ide_device_manager_create_runner_finish(IdeDeviceManager * self,GAsyncResult * result,GError ** error)1196 ide_device_manager_create_runner_finish (IdeDeviceManager  *self,
1197                                          GAsyncResult      *result,
1198                                          GError           **error)
1199 {
1200   IdeRunner *ret;
1201 
1202   IDE_ENTRY;
1203 
1204   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), FALSE);
1205   g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
1206   g_return_val_if_fail (ide_task_is_valid (IDE_TASK (result), self), FALSE);
1207 
1208   ret = ide_task_propagate_pointer (IDE_TASK (result), error);
1209 
1210   IDE_RETURN (ret);
1211 }
1212 
1213 gdouble
ide_device_manager_get_progress(IdeDeviceManager * self)1214 ide_device_manager_get_progress (IdeDeviceManager *self)
1215 {
1216   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), 0.0);
1217 
1218   return self->progress;
1219 }
1220 
1221 GMenu *
_ide_device_manager_get_menu(IdeDeviceManager * self)1222 _ide_device_manager_get_menu (IdeDeviceManager *self)
1223 {
1224   g_return_val_if_fail (IDE_IS_DEVICE_MANAGER (self), NULL);
1225 
1226   return self->menu;
1227 }
1228 
1229 static void
ide_device_manager_init_provider_load_cb(GObject * object,GAsyncResult * result,gpointer user_data)1230 ide_device_manager_init_provider_load_cb (GObject      *object,
1231                                           GAsyncResult *result,
1232                                           gpointer      user_data)
1233 {
1234   IdeDeviceProvider *provider = (IdeDeviceProvider *)object;
1235   g_autoptr(IdeTask) task = user_data;
1236   g_autoptr(GError) error = NULL;
1237   InitState *state;
1238 
1239   g_assert (IDE_IS_MAIN_THREAD ());
1240   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
1241   g_assert (G_IS_ASYNC_RESULT (result));
1242   g_assert (IDE_IS_TASK (task));
1243 
1244   if (!ide_device_provider_load_finish (provider, result, &error))
1245     g_warning ("%s: %s", G_OBJECT_TYPE_NAME (provider), error->message);
1246 
1247   state = ide_task_get_task_data (task);
1248   state->n_active--;
1249 
1250   if (state->n_active == 0)
1251     ide_task_return_boolean (task, TRUE);
1252 }
1253 
1254 static void
ide_device_manager_init_provider_cb(IdeExtensionSetAdapter * set,PeasPluginInfo * plugin_info,PeasExtension * exten,gpointer user_data)1255 ide_device_manager_init_provider_cb (IdeExtensionSetAdapter *set,
1256                                      PeasPluginInfo         *plugin_info,
1257                                      PeasExtension          *exten,
1258                                      gpointer                user_data)
1259 {
1260   IdeDeviceProvider *provider = (IdeDeviceProvider *)exten;
1261   IdeDeviceManager *self;
1262   IdeTask *task = user_data;
1263   InitState *state;
1264 
1265   g_assert (IDE_IS_MAIN_THREAD ());
1266   g_assert (IDE_IS_EXTENSION_SET_ADAPTER (set));
1267   g_assert (plugin_info != NULL);
1268   g_assert (IDE_IS_DEVICE_PROVIDER (provider));
1269   g_assert (IDE_IS_TASK (task));
1270 
1271   self = ide_task_get_source_object (task);
1272   state = ide_task_get_task_data (task);
1273   state->n_active++;
1274 
1275   g_signal_connect_object (provider,
1276                            "device-added",
1277                            G_CALLBACK (ide_device_manager_provider_device_added_cb),
1278                            self,
1279                            G_CONNECT_SWAPPED);
1280 
1281   g_signal_connect_object (provider,
1282                            "device-removed",
1283                            G_CALLBACK (ide_device_manager_provider_device_removed_cb),
1284                            self,
1285                            G_CONNECT_SWAPPED);
1286 
1287   ide_device_provider_load_async (provider,
1288                                   ide_task_get_cancellable (task),
1289                                   ide_device_manager_init_provider_load_cb,
1290                                   g_object_ref (task));
1291 }
1292 
1293 static void
ide_device_manager_init_async(GAsyncInitable * initable,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1294 ide_device_manager_init_async (GAsyncInitable      *initable,
1295                                gint                 io_priority,
1296                                GCancellable        *cancellable,
1297                                GAsyncReadyCallback  callback,
1298                                gpointer             user_data)
1299 {
1300   IdeDeviceManager *self = (IdeDeviceManager *)initable;
1301   g_autoptr(IdeTask) task = NULL;
1302   InitState *state;
1303 
1304   IDE_ENTRY;
1305 
1306   g_assert (IDE_IS_MAIN_THREAD ());
1307   g_assert (IDE_IS_DEVICE_MANAGER (self));
1308   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
1309 
1310   task = ide_task_new (self, cancellable, callback, user_data);
1311   ide_task_set_source_tag (task, ide_device_manager_init_async);
1312   ide_task_set_priority (task, io_priority);
1313 
1314   self->loading = TRUE;
1315 
1316   state = g_new0 (InitState, 1);
1317   ide_task_set_task_data (task, state, g_free);
1318 
1319   ide_device_manager_add_local (self);
1320 
1321   self->providers = ide_extension_set_adapter_new (IDE_OBJECT (self),
1322                                                    peas_engine_get_default (),
1323                                                    IDE_TYPE_DEVICE_PROVIDER,
1324                                                    NULL, NULL);
1325 
1326   g_signal_connect (self->providers,
1327                     "extension-added",
1328                     G_CALLBACK (ide_device_manager_provider_added_cb),
1329                     self);
1330 
1331   g_signal_connect (self->providers,
1332                     "extension-removed",
1333                     G_CALLBACK (ide_device_manager_provider_removed_cb),
1334                     self);
1335 
1336   ide_extension_set_adapter_foreach (self->providers,
1337                                      ide_device_manager_init_provider_cb,
1338                                      task);
1339 
1340   if (state->n_active == 0)
1341     ide_task_return_boolean (task, TRUE);
1342 
1343   IDE_EXIT;
1344 }
1345 
1346 static gboolean
ide_device_manager_init_finish(GAsyncInitable * initable,GAsyncResult * result,GError ** error)1347 ide_device_manager_init_finish (GAsyncInitable  *initable,
1348                                 GAsyncResult    *result,
1349                                 GError         **error)
1350 {
1351   IdeDeviceManager *self = (IdeDeviceManager *)initable;
1352   gboolean ret;
1353 
1354   IDE_ENTRY;
1355 
1356   g_assert (IDE_IS_MAIN_THREAD ());
1357   g_assert (IDE_IS_DEVICE_MANAGER (initable));
1358   g_assert (IDE_IS_TASK (result));
1359 
1360   self->loading = FALSE;
1361 
1362   ret = ide_task_propagate_boolean (IDE_TASK (result), error);
1363 
1364   IDE_RETURN (ret);
1365 }
1366 
1367 static void
async_initable_init_iface(GAsyncInitableIface * iface)1368 async_initable_init_iface (GAsyncInitableIface *iface)
1369 {
1370   iface->init_async = ide_device_manager_init_async;
1371   iface->init_finish = ide_device_manager_init_finish;
1372 }
1373