[gnome-software] Move the launch action to the plugin loader



commit 15b6e5d07cc75003321b570e35c7353ca1049499
Author: Richard Hughes <richard hughsie com>
Date:   Tue Dec 22 09:05:42 2015 +0000

    Move the launch action to the plugin loader
    
    This allows to handle different kinds of application in the future (e.g. for
    xdg-apps) and possibly also allows us to handle web-apps in a smarter way.

 src/gs-cmd.c                       |   12 ++++-
 src/gs-plugin-loader-sync.c        |   48 ++++++++++++++++
 src/gs-plugin-loader-sync.h        |    4 +
 src/gs-plugin-loader.c             |  109 ++++++++++++++++++++++++++++++++++++
 src/gs-plugin-loader.h             |    8 +++
 src/gs-plugin.h                    |    8 +++
 src/gs-shell-details.c             |   40 +++++++-------
 src/plugins/gs-plugin-epiphany.c   |   42 ++++++++++++++
 src/plugins/gs-plugin-packagekit.c |   42 ++++++++++++++
 9 files changed, 292 insertions(+), 21 deletions(-)
---
diff --git a/src/gs-cmd.c b/src/gs-cmd.c
index a23d0ef..3e013d4 100644
--- a/src/gs-cmd.c
+++ b/src/gs-cmd.c
@@ -298,6 +298,16 @@ main (int argc, char **argv)
                                break;
                }
                gs_plugin_add_app (&list, app);
+       } else if (argc == 3 && g_strcmp0 (argv[1], "launch") == 0) {
+               app = gs_app_new (argv[2]);
+               for (i = 0; i < repeat; i++) {
+                       ret = gs_plugin_loader_app_launch (plugin_loader,
+                                                          app,
+                                                          NULL,
+                                                          &error);
+                       if (!ret)
+                               break;
+               }
        } else if (argc == 3 && g_strcmp0 (argv[1], "filename-to-app") == 0) {
                app = gs_plugin_loader_filename_to_app (plugin_loader,
                                                        argv[2],
@@ -417,7 +427,7 @@ main (int argc, char **argv)
                                     "Did not recognise option, use 'installed', "
                                     "'updates', 'popular', 'get-categories', "
                                     "'get-category-apps', 'filename-to-app', "
-                                    "'sources', 'refresh' or 'search'");
+                                    "'sources', 'refresh', 'launch' or 'search'");
        }
        if (!ret) {
                g_print ("Failed: %s\n", error->message);
diff --git a/src/gs-plugin-loader-sync.c b/src/gs-plugin-loader-sync.c
index 254b8b9..f50fc9b 100644
--- a/src/gs-plugin-loader-sync.c
+++ b/src/gs-plugin-loader-sync.c
@@ -515,6 +515,54 @@ gs_plugin_loader_app_refine (GsPluginLoader *plugin_loader,
 }
 
 /**
+ * gs_plugin_loader_app_launch_finish_sync:
+ **/
+static void
+gs_plugin_loader_app_launch_finish_sync (GsPluginLoader *plugin_loader,
+                                        GAsyncResult *res,
+                                        GsPluginLoaderHelper *helper)
+{
+       helper->ret = gs_plugin_loader_app_launch_finish (plugin_loader,
+                                                         res,
+                                                         helper->error);
+       g_main_loop_quit (helper->loop);
+}
+
+/**
+ * gs_plugin_loader_app_launch:
+ **/
+gboolean
+gs_plugin_loader_app_launch (GsPluginLoader *plugin_loader,
+                            GsApp *app,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GsPluginLoaderHelper helper;
+
+       /* create temp object */
+       helper.context = g_main_context_new ();
+       helper.loop = g_main_loop_new (helper.context, FALSE);
+       helper.error = error;
+
+       g_main_context_push_thread_default (helper.context);
+
+       /* run async method */
+       gs_plugin_loader_app_launch_async (plugin_loader,
+                                          app,
+                                          cancellable,
+                                          (GAsyncReadyCallback) gs_plugin_loader_app_launch_finish_sync,
+                                          &helper);
+       g_main_loop_run (helper.loop);
+
+       g_main_context_pop_thread_default (helper.context);
+
+       g_main_loop_unref (helper.loop);
+       g_main_context_unref (helper.context);
+
+       return helper.ret;
+}
+
+/**
  * gs_plugin_loader_app_action_finish_sync:
  **/
 static void
diff --git a/src/gs-plugin-loader-sync.h b/src/gs-plugin-loader-sync.h
index b1b88ec..90d65c0 100644
--- a/src/gs-plugin-loader-sync.h
+++ b/src/gs-plugin-loader-sync.h
@@ -71,6 +71,10 @@ gboolean      gs_plugin_loader_app_refine            (GsPluginLoader *plugin_loader,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_loader_app_launch            (GsPluginLoader *plugin_loader,
+                                                        GsApp          *app,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 gboolean        gs_plugin_loader_app_action            (GsPluginLoader *plugin_loader,
                                                         GsApp          *app,
                                                         GsPluginLoaderAction action,
diff --git a/src/gs-plugin-loader.c b/src/gs-plugin-loader.c
index 5d13889..d938d27 100644
--- a/src/gs-plugin-loader.c
+++ b/src/gs-plugin-loader.c
@@ -348,6 +348,37 @@ out:
 }
 
 /**
+ * gs_plugin_loader_run_launch:
+ **/
+static gboolean
+gs_plugin_loader_run_launch (GsPluginLoader *plugin_loader,
+                            GsApp *app,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       GsPluginLoaderPrivate *priv = gs_plugin_loader_get_instance_private (plugin_loader);
+       GsPluginLaunchFunc plugin_func = NULL;
+       GsPlugin *plugin;
+       gboolean ret = TRUE;
+       guint i;
+
+       /* run each plugin */
+       for (i = 0; i < priv->plugins->len; i++) {
+               plugin = g_ptr_array_index (priv->plugins, i);
+               if (!plugin->enabled)
+                       continue;
+               if (!g_module_symbol (plugin->module,
+                                     "gs_plugin_launch",
+                                     (gpointer *) &plugin_func))
+                       continue;
+               ret = plugin_func (plugin, app, cancellable, error);
+               if (!ret)
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+/**
  * gs_plugin_loader_run_results_plugin:
  **/
 static gboolean
@@ -2306,6 +2337,84 @@ gs_plugin_loader_app_refine_finish (GsPluginLoader *plugin_loader,
 
 /******************************************************************************/
 
+/**
+ * gs_plugin_loader_app_launch_thread_cb:
+ **/
+static void
+gs_plugin_loader_app_launch_thread_cb (GTask *task,
+                                      gpointer object,
+                                      gpointer task_data,
+                                      GCancellable *cancellable)
+{
+       GError *error = NULL;
+       GsPluginLoaderAsyncState *state = (GsPluginLoaderAsyncState *) task_data;
+       GsPluginLoader *plugin_loader = GS_PLUGIN_LOADER (object);
+       gboolean ret;
+
+       ret = gs_plugin_loader_run_launch (plugin_loader,
+                                          state->app,
+                                          cancellable,
+                                          &error);
+       if (!ret) {
+               g_task_return_error (task, error);
+               return;
+       }
+
+       /* success */
+       g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * gs_plugin_loader_app_launch_async:
+ *
+ * This method calls all plugins that implement the gs_plugin_launch()
+ * function.
+ **/
+void
+gs_plugin_loader_app_launch_async (GsPluginLoader *plugin_loader,
+                                  GsApp *app,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+       GsPluginLoaderAsyncState *state;
+       g_autoptr(GTask) task = NULL;
+
+       g_return_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader));
+       g_return_if_fail (GS_IS_APP (app));
+       g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+       /* save state */
+       state = g_slice_new0 (GsPluginLoaderAsyncState);
+       state->app = g_object_ref (app);
+
+       /* run in a thread */
+       task = g_task_new (plugin_loader, cancellable, callback, user_data);
+       g_task_set_task_data (task, state, (GDestroyNotify) gs_plugin_loader_free_async_state);
+       g_task_set_return_on_cancel (task, TRUE);
+       g_task_run_in_thread (task, gs_plugin_loader_app_launch_thread_cb);
+}
+
+/**
+ * gs_plugin_loader_app_launch_finish:
+ *
+ * Return value: success
+ **/
+gboolean
+gs_plugin_loader_app_launch_finish (GsPluginLoader *plugin_loader,
+                                   GAsyncResult *res,
+                                   GError **error)
+{
+       g_return_val_if_fail (GS_IS_PLUGIN_LOADER (plugin_loader), FALSE);
+       g_return_val_if_fail (G_IS_TASK (res), FALSE);
+       g_return_val_if_fail (g_task_is_valid (res, plugin_loader), FALSE);
+       g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+       return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+/******************************************************************************/
+
 static gboolean
 emit_pending_apps_idle (gpointer loader)
 {
diff --git a/src/gs-plugin-loader.h b/src/gs-plugin-loader.h
index dd034f1..133760c 100644
--- a/src/gs-plugin-loader.h
+++ b/src/gs-plugin-loader.h
@@ -198,6 +198,14 @@ void                gs_plugin_loader_app_refine_async      (GsPluginLoader 
*plugin_loader,
 gboolean        gs_plugin_loader_app_refine_finish     (GsPluginLoader *plugin_loader,
                                                         GAsyncResult   *res,
                                                         GError         **error);
+void            gs_plugin_loader_app_launch_async      (GsPluginLoader *plugin_loader,
+                                                        GsApp          *app,
+                                                        GCancellable   *cancellable,
+                                                        GAsyncReadyCallback callback,
+                                                        gpointer        user_data);
+gboolean        gs_plugin_loader_app_launch_finish     (GsPluginLoader *plugin_loader,
+                                                        GAsyncResult   *res,
+                                                        GError         **error);
 void            gs_plugin_loader_app_action_async      (GsPluginLoader *plugin_loader,
                                                         GsApp          *app,
                                                         GsPluginLoaderAction a,
diff --git a/src/gs-plugin.h b/src/gs-plugin.h
index 0c9ae09..aa30144 100644
--- a/src/gs-plugin.h
+++ b/src/gs-plugin.h
@@ -143,6 +143,10 @@ typedef gboolean    (*GsPluginRefineFunc)          (GsPlugin       *plugin,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+typedef gboolean        (*GsPluginLaunchFunc)          (GsPlugin       *plugin,
+                                                        GsApp          *app,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 typedef gboolean        (*GsPluginRefreshFunc  )       (GsPlugin       *plugin,
                                                         guint           cache_age,
                                                         GsPluginRefreshFlags flags,
@@ -241,6 +245,10 @@ gboolean    gs_plugin_refine                       (GsPlugin       *plugin,
                                                         GsPluginRefineFlags flags,
                                                         GCancellable   *cancellable,
                                                         GError         **error);
+gboolean        gs_plugin_launch                       (GsPlugin       *plugin,
+                                                        GsApp          *app,
+                                                        GCancellable   *cancellable,
+                                                        GError         **error);
 gboolean        gs_plugin_app_install                  (GsPlugin       *plugin,
                                                         GsApp          *app,
                                                         GCancellable   *cancellable,
diff --git a/src/gs-shell-details.c b/src/gs-shell-details.c
index 2c2c57f..da20d1b 100644
--- a/src/gs-shell-details.c
+++ b/src/gs-shell-details.c
@@ -24,7 +24,6 @@
 
 #include <string.h>
 #include <glib/gi18n.h>
-#include <gio/gdesktopappinfo.h>
 #include <appstream-glib.h>
 
 #include "gs-utils.h"
@@ -1201,31 +1200,32 @@ gs_shell_details_addon_selected_cb (GsAppAddonRow *row,
 }
 
 /**
- * gs_shell_details_app_launch_button_cb:
+ * gs_shell_details_filename_to_app_cb:
  **/
 static void
-gs_shell_details_app_launch_button_cb (GtkWidget *widget, GsShellDetails *self)
+gs_shell_details_app_launch_cb (GObject *source,
+                               GAsyncResult *res,
+                               gpointer user_data)
 {
-       GdkDisplay *display;
-       const gchar *desktop_id;
+       GsShellDetails *self = GS_SHELL_DETAILS (user_data);
        g_autoptr(GError) error = NULL;
-       g_autoptr(GAppInfo) appinfo = NULL;
-       g_autoptr(GAppLaunchContext) context = NULL;
-
-       desktop_id = gs_app_get_id (self->app);
-       if (desktop_id == NULL) {
-               g_warning ("no such desktop file: %s", desktop_id);
+       if (!gs_plugin_loader_app_launch_finish (self->plugin_loader, res, &error)) {
+               g_warning ("failed to launch GsApp: %s", error->message);
                return;
        }
-       appinfo = G_APP_INFO (g_desktop_app_info_new (desktop_id));
-       if (appinfo == NULL) {
-               g_warning ("no such desktop file: %s", desktop_id);
-               return;
-       }
-       display = gdk_display_get_default ();
-       context = G_APP_LAUNCH_CONTEXT (gdk_display_get_app_launch_context (display));
-       if (!g_app_info_launch (appinfo, NULL, context, &error))
-               g_warning ("launching %s failed: %s", desktop_id, error->message);
+}
+
+/**
+ * gs_shell_details_app_launch_button_cb:
+ **/
+static void
+gs_shell_details_app_launch_button_cb (GtkWidget *widget, GsShellDetails *self)
+{
+       gs_plugin_loader_app_launch_async (self->plugin_loader,
+                                          self->app,
+                                          self->cancellable,
+                                          gs_shell_details_app_launch_cb,
+                                          self);
 }
 
 /**
diff --git a/src/plugins/gs-plugin-epiphany.c b/src/plugins/gs-plugin-epiphany.c
index 1d32afb..c711efb 100644
--- a/src/plugins/gs-plugin-epiphany.c
+++ b/src/plugins/gs-plugin-epiphany.c
@@ -25,6 +25,7 @@
 #include <string.h>
 
 #include <glib/gi18n.h>
+#include <gio/gdesktopappinfo.h>
 #include <libsoup/soup.h>
 
 #include <gs-plugin.h>
@@ -295,3 +296,44 @@ gs_plugin_refine (GsPlugin *plugin,
        }
        return TRUE;
 }
+
+/**
+ * gs_plugin_launch:
+ */
+gboolean
+gs_plugin_launch (GsPlugin *plugin,
+                 GsApp *app,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       GdkDisplay *display;
+       const gchar *desktop_id;
+       g_autoptr(GAppInfo) appinfo = NULL;
+       g_autoptr(GAppLaunchContext) context = NULL;
+
+       /* only process this app if was created by this plugin */
+       if (g_strcmp0 (gs_app_get_management_plugin (app), "Epiphany") != 0)
+               return TRUE;
+
+       desktop_id = gs_app_get_id (app);
+       if (desktop_id == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                            "no such desktop file: %s",
+                            desktop_id);
+               return FALSE;
+       }
+       appinfo = G_APP_INFO (g_desktop_app_info_new (desktop_id));
+       if (appinfo == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                            "no such desktop file: %s",
+                            desktop_id);
+               return FALSE;
+       }
+       display = gdk_display_get_default ();
+       context = G_APP_LAUNCH_CONTEXT (gdk_display_get_app_launch_context (display));
+       return g_app_info_launch (appinfo, NULL, context, error);
+}
diff --git a/src/plugins/gs-plugin-packagekit.c b/src/plugins/gs-plugin-packagekit.c
index 48841f5..4269827 100644
--- a/src/plugins/gs-plugin-packagekit.c
+++ b/src/plugins/gs-plugin-packagekit.c
@@ -24,6 +24,7 @@
 #define I_KNOW_THE_PACKAGEKIT_GLIB2_API_IS_SUBJECT_TO_CHANGE
 #include <packagekit-glib2/packagekit.h>
 #include <glib/gi18n.h>
+#include <gio/gdesktopappinfo.h>
 
 #include <gs-plugin.h>
 
@@ -692,3 +693,44 @@ gs_plugin_add_search_what_provides (GsPlugin *plugin,
        /* add results */
        return gs_plugin_packagekit_add_results (plugin, list, results, error);
 }
+
+/**
+ * gs_plugin_launch:
+ */
+gboolean
+gs_plugin_launch (GsPlugin *plugin,
+                 GsApp *app,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+       GdkDisplay *display;
+       const gchar *desktop_id;
+       g_autoptr(GAppInfo) appinfo = NULL;
+       g_autoptr(GAppLaunchContext) context = NULL;
+
+       /* only process this app if was created by this plugin */
+       if (g_strcmp0 (gs_app_get_management_plugin (app), "PackageKit") != 0)
+               return TRUE;
+
+       desktop_id = gs_app_get_id (app);
+       if (desktop_id == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                            "no such desktop file: %s",
+                            desktop_id);
+               return FALSE;
+       }
+       appinfo = G_APP_INFO (g_desktop_app_info_new (desktop_id));
+       if (appinfo == NULL) {
+               g_set_error (error,
+                            GS_PLUGIN_ERROR,
+                            GS_PLUGIN_ERROR_NOT_SUPPORTED,
+                            "no such desktop file: %s",
+                            desktop_id);
+               return FALSE;
+       }
+       display = gdk_display_get_default ();
+       context = G_APP_LAUNCH_CONTEXT (gdk_display_get_app_launch_context (display));
+       return g_app_info_launch (appinfo, NULL, context, error);
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]