--- /dev/null
+<refentry id="gapplication-tool" lang="en">
+ <refentryinfo>
+ <title>gapplication</title>
+ <productname>GIO</productname>
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>Ryan</firstname>
+ <surname>Lortie</surname>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>gapplication</refentrytitle>
+ <manvolnum>1</manvolnum>
+ <refmiscinfo class="manual">User Commands</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>gapplication</refname>
+ <refpurpose>D-Bus application launcher</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">help</arg>
+ <arg choice="opt"><replaceable>COMMAND</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">version</arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">list-apps</arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">launch</arg>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">launch</arg>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ <arg choice="opt" rep="repeat"><replaceable>FILE</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">list-actions</arg>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ </cmdsynopsis>
+ <cmdsynopsis>
+ <command>gapplication</command>
+ <arg choice="plain">action</arg>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ <arg choice="plain"><replaceable>ACTION</replaceable></arg>
+ <arg choice="opt"><replaceable>PARAMETER</replaceable></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para>
+ <command>gapplication</command> is a commandline implementation of the client-side of the
+ <interfacename>org.freedesktop.Application</interfacename> interface as specified by the freedesktop.org
+ Desktop Entry Specification.
+ </para>
+
+ <para>
+ <command>gapplication</command> can be used to start applications that have
+ <varname>DBusActivatable</varname> set to <literal>true</literal> in their <filename>.desktop</filename>
+ files and can be used to send messages to already-running instances of other applications.
+ </para>
+
+ <para>
+ It is possible for applications to refer to <command>gapplication</command> in the <varname>Exec</varname>
+ line of their <filename class='extension'>.desktop</filename> file to maintain backwards compatibility
+ with implementations that do not directly support <varname>DBusActivatable</varname>.
+ </para>
+
+ <para>
+ <command>gapplication</command> ships as part of <application>GLib</application>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>Commands</title>
+
+ <refsect2>
+ <title>Global commands</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>
+ <command>help</command>
+ <arg choice="opt"><replaceable>COMMAND</replaceable></arg>
+ </term>
+ <listitem>
+ <para>
+ Displays a short synopsis of the available commands or provides detailed help on a specific
+ command.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>version</command>
+ </term>
+ <listitem>
+ <para>
+ Prints the GLib version whence <command>gapplication</command> came.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>list-apps</command>
+ </term>
+ <listitem>
+ <para>
+ Prints a list of all application IDs that are known to support D-Bus activation. This list is
+ generated by scanning <filename class='extension'>.desktop</filename> files as per the current
+ <envar>XDG_DATA_DIRS</envar>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>launch</command>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ <arg choice="opt" rep="repeat"><replaceable>FILE</replaceable></arg>
+ </term>
+ <listitem>
+ <para>
+ Launches an application.
+ </para>
+ <para>
+ The first parameter is the application ID in the familiar "reverse DNS" style (eg:
+ '<literal>org.gnome.app</literal>') without the <filename class='extension'>.desktop</filename>
+ suffix.
+ </para>
+ <para>
+ Optionally, if additional parameters are given, they are treated as the names of files to open and
+ may be filenames or URIs. If no files are given then the application is simply activated.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>list-actions</command>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ </term>
+ <listitem>
+ <para>
+ List the actions declared in the application's <filename class='extension'>.desktop</filename>
+ file. The parameter is the application ID, as above.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <command>action</command>
+ <arg choice="plain"><replaceable>APPID</replaceable></arg>
+ <arg choice="plain"><replaceable>ACTION</replaceable></arg>
+ <arg choice="opt"><replaceable>PARAMETER</replaceable></arg>
+ </term>
+ <listitem>
+ <para>
+ Invokes the named action (in the same way as would occur when activating an action specified in
+ the <filename class='extension'>.desktop</filename> file).
+ </para>
+ <para>
+ The application ID (as above) is the first parameter. The action name follows.
+ </para>
+ <para>
+ Optionally, following the action name can be one parameter, in GVariant format, given as a single
+ argument. Make sure to use sufficient quoting.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect2>
+ </refsect1>
+
+ <refsect1>
+ <title>Examples</title>
+
+ <refsect2>
+ <title>From the commandline</title>
+
+ <para>
+ Launching an application:
+ </para>
+
+ <programlisting>
+ gapplication launch org.example.fooview
+ </programlisting>
+
+ <para>
+ Opening a file with an application:
+ </para>
+
+ <programlisting>
+ gapplication launch org.example.fooview ~/file.foo
+ </programlisting>
+
+ <para>
+ Opening many files with an application:
+ </para>
+
+ <programlisting>
+ gapplication launch org.example.fooview ~/foos/*.foo
+ </programlisting>
+
+ <para>
+ Invoking an action on an application:
+ </para>
+
+ <programlisting>
+ gapplication action org.example.fooview create
+ </programlisting>
+
+ <para>
+ Invoking an action on an application, with an action:
+ </para>
+
+ <programlisting>
+ gapplication action org.example.fooview show-item '"item_id_828739"'
+ </programlisting>
+ </refsect2>
+
+ <refsect2>
+ <title>
+ From the <varname>Exec</varname> lines of a <filename class='extension'>.desktop</filename> file
+ </title>
+
+ <para>
+ The commandline interface of <command>gapplication</command> was designed so that it could be used
+ directly from the <varname>Exec</varname> line of a <filename class='extension'>.desktop</filename>
+ file.
+ </para>
+
+ <para>
+ You might want to do this to allow for backwards compatibility with implementations of the specification
+ that do not understand how to do D-Bus activation, without having to install a separate utility program.
+ </para>
+
+ <para>
+ Consider the following example:
+ </para>
+
+ <programlisting>
+ [Desktop Entry]
+ Version=1.1
+ Type=Application
+ Name=Foo Viewer
+ DBusActivatable=true
+ MimeType=image/x-foo;
+ Exec=gapplication launch org.example.fooview %F
+ Actions=gallery;create;
+
+ [Desktop Action gallery]
+ Name=Browse Gallery
+ Exec=gapplication action org.example.fooview gallery
+
+ [Desktop Action create]
+ Name=Create a new Foo!
+ Exec=gapplication action org.example.fooview create
+ </programlisting>
+ </refsect2>
+
+ <refsect2>
+ <title>From a script</title>
+
+ <para>
+ If installing an application that supports D-Bus activation you may still want to put a file in
+ <filename class='directory'>/usr/bin</filename> so that your program can be started from a terminal.
+ </para>
+
+ <para>
+ It is possible for this file to be a shell script. The script can handle arguments such as --help and
+ --version directly. It can also parse other command line arguments and convert them to uses of
+ <command>gapplication</command> to activate the application, open files, or invoke actions.
+ </para>
+
+ <para>
+ Here is a simplified example, as may be installed in <filename>/usr/bin/fooview</filename>:
+ </para>
+
+ <programlisting>
+ #!/bin/sh
+
+ case "$1" in
+ --help)
+ echo "see 'man fooview' for more information"
+ ;;
+
+ --version)
+ echo "fooview 1.2"
+ ;;
+
+ --gallery)
+ gapplication action org.example.fooview gallery
+ ;;
+
+ --create)
+ gapplication action org.example.fooview create
+ ;;
+
+ -*)
+ echo "unrecognised commandline argument"
+ exit 1
+ ;;
+
+ *)
+ gapplication launch org.example.fooview "$@"
+ ;;
+ esac
+ </programlisting>
+ </refsect2>
+ </refsect1>
+
+ <refsect1>
+ <title>See also</title>
+ <para>
+ <ulink url='http://standards.freedesktop.org/desktop-entry-spec/latest/'>Desktop Entry Specification</ulink>,
+ <citerefentry>
+ <refentrytitle>gdbus</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>xdg-open</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>desktop-file-validate</refentrytitle>
+ <manvolnum>1</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
--- /dev/null
+/*
+ * Copyright © 2013 Canonical Limited
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the licence, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Ryan Lortie <desrt@desrt.ca>
+ */
+
+#include "config.h"
+
+#include <gio/gdesktopappinfo.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include <string.h>
+#include <locale.h>
+
+struct help_topic
+{
+ const gchar *command;
+ const gchar *summary;
+ const gchar *description;
+ const gchar *synopsis;
+};
+
+struct help_substvar
+{
+ const gchar *var;
+ const gchar *description;
+};
+
+static const struct help_topic topics[] = {
+ { "help", N_("Print help"),
+ N_("Print help"),
+ N_("[COMMAND]")
+ },
+ { "version", N_("Print version"),
+ N_("Print version information and exit")
+ },
+ { "list-apps", N_("List applications"),
+ N_("List the installed D-Bus activatable applications (by .desktop files)")
+ },
+ { "launch", N_("Launch an application"),
+ N_("Launch the application (with optional files to open)"),
+ N_("APPID [FILE...]")
+ },
+ { "action", N_("Activate an action"),
+ N_("Invoke an action on the application"),
+ N_("APPID ACTION [PARAMETER]")
+ },
+ { "list-actions", N_("List available actions"),
+ N_("List static actions for an application (from .desktop file)"),
+ N_("APPID")
+ }
+};
+
+static const struct help_substvar substvars[] = {
+ { N_("COMMAND"), N_("The command to print detailed help for") },
+ { N_("APPID"), N_("Application identifier in D-Bus format (eg: org.example.viewer)") },
+ { N_("FILE"), N_("Optional relative or relative filenames, or URIs to open") },
+ { N_("ACTION"), N_("The action name to invoke") },
+ { N_("PARAMETER"), N_("Optional parameter to the action invocation, in GVariant format") }
+};
+
+static int
+app_help (gboolean requested,
+ const gchar *command)
+{
+ const struct help_topic *topic = NULL;
+ GString *string;
+
+ string = g_string_new (NULL);
+
+ if (command)
+ {
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (topics); i++)
+ if (g_str_equal (topics[i].command, command))
+ topic = &topics[i];
+
+ if (!topic)
+ {
+ g_string_printf (string, _("Unknown command %s\n\n"), command);
+ requested = FALSE;
+ }
+ }
+
+ g_string_append (string, _("Usage:\n"));
+
+ if (topic)
+ {
+ gint maxwidth;
+ gint i;
+
+ g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication",
+ topic->command, topic->synopsis ? _(topic->synopsis) : "");
+ g_string_append_printf (string, "%s\n\n", _(topic->description));
+
+ if (topic->synopsis)
+ {
+ g_string_append (string, _("Arguments:\n"));
+
+ maxwidth = 0;
+ for (i = 0; i < G_N_ELEMENTS (substvars); i++)
+ if (strstr (topic->synopsis, substvars[i].var))
+ maxwidth = MAX(maxwidth, strlen (_(substvars[i].var)));
+
+ for (i = 0; i < G_N_ELEMENTS (substvars); i++)
+ if (strstr (topic->synopsis, substvars[i].var))
+ g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
+ _(substvars[i].var), _(substvars[i].description));
+ g_string_append (string, "\n");
+ }
+ }
+ else
+ {
+ gint maxwidth;
+ gint i;
+
+ g_string_append_printf (string, "\n %s %s %s\n\n", "gapplication", _("COMMAND"), _("[ARGS...]"));
+ g_string_append_printf (string, _("Commands:\n"));
+
+ maxwidth = 0;
+ for (i = 0; i < G_N_ELEMENTS (topics); i++)
+ maxwidth = MAX(maxwidth, strlen (topics[i].command));
+
+ for (i = 0; i < G_N_ELEMENTS (topics); i++)
+ g_string_append_printf (string, " %-*.*s %s\n", maxwidth, maxwidth,
+ topics[i].command, _(topics[i].summary));
+
+ g_string_append (string, "\n");
+ /* Translators: do not translate 'help', but please translate 'COMMAND'. */
+ g_string_append_printf (string, _("Use '%s help COMMAND' to get detailed help.\n\n"), "gapplication");
+ }
+
+ if (requested)
+ g_print ("%s", string->str);
+ else
+ g_printerr ("%s\n", string->str);
+
+ g_string_free (string, TRUE);
+
+ return requested ? 0 : 1;
+}
+
+static gboolean
+app_check_name (gchar **args,
+ const gchar *command)
+{
+ if (args[0] == NULL)
+ {
+ g_printerr (_("%s command requires an application id to directly follow\n\n"), command);
+ return FALSE;
+ }
+
+ if (!g_dbus_is_name (args[0]))
+ {
+ g_printerr (_("invalid application id: '%s'\n"), args[0]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int
+app_no_args (const gchar *command)
+{
+ /* Translators: %s is replaced with a command name like 'list-actions' */
+ g_printerr (_("'%s' takes no arguments\n\n"), command);
+ return app_help (FALSE, command);
+}
+
+static int
+app_version (gchar **args)
+{
+ if (g_strv_length (args))
+ return app_no_args ("version");
+
+ g_print (PACKAGE_VERSION "\n");
+ return 0;
+}
+
+static int
+app_list (gchar **args)
+{
+ GList *apps;
+
+ if (g_strv_length (args))
+ return app_no_args ("list");
+
+ apps = g_app_info_get_all ();
+
+ while (apps)
+ {
+ GDesktopAppInfo *info = apps->data;
+
+ if (G_IS_DESKTOP_APP_INFO (info))
+ if (g_desktop_app_info_get_boolean (info, "DBusActivatable"))
+ {
+ const gchar *filename;
+
+ filename = g_app_info_get_id (G_APP_INFO (info));
+ if (g_str_has_suffix (filename, ".desktop"))
+ {
+ gchar *id;
+
+ id = g_strndup (filename, strlen (filename) - 8);
+ g_print ("%s\n", id);
+ g_free (id);
+ }
+ }
+
+ apps = g_list_delete_link (apps, apps);
+ g_object_unref (info);
+ }
+
+ return 0;
+}
+
+static gchar *
+app_path_for_id (const gchar *app_id)
+{
+ gchar *path;
+ gint i;
+
+ path = g_strconcat ("/", app_id, NULL);
+ for (i = 0; path[i]; i++)
+ if (path[i] == '.')
+ path[i] = '/';
+
+ return path;
+}
+
+static int
+app_call (const gchar *app_id,
+ const gchar *method_name,
+ GVariant *parameters)
+{
+ GDBusConnection *session;
+ GError *error = NULL;
+ gchar *object_path;
+ GVariant *result;
+
+
+ session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (!session)
+ {
+ g_variant_unref (g_variant_ref_sink (parameters));
+ g_printerr (_("unable to connect to D-Bus: %s\n"), error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ object_path = app_path_for_id (app_id);
+
+ result = g_dbus_connection_call_sync (session, app_id, object_path, "org.freedesktop.Application",
+ method_name, parameters, G_VARIANT_TYPE_UNIT,
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+ g_free (object_path);
+
+ if (result)
+ {
+ g_variant_unref (result);
+ return 0;
+ }
+ else
+ {
+ g_printerr (_("error sending %s message to application: %s\n"), method_name, error->message);
+ g_error_free (error);
+ return 1;
+ }
+}
+
+static GVariant *
+app_get_platform_data (void)
+{
+ GVariantBuilder builder;
+ const gchar *startup_id;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+
+ if ((startup_id = g_getenv ("DESKTOP_STARTUP_iD")))
+ g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_string (startup_id));
+
+ return g_variant_builder_end (&builder);
+}
+
+static int
+app_action (gchar **args)
+{
+ GVariantBuilder params;
+ const gchar *name;
+
+ if (!app_check_name (args, "action"))
+ return 1;
+
+ if (args[1] == NULL)
+ {
+ g_printerr (_("action name must be given after application id\n"));
+ return 1;
+ }
+
+ name = args[1];
+
+ if (!g_action_name_is_valid (name))
+ {
+ g_printerr (_("invalid action name: '%s'\n"
+ "action names must consist of only alphanumerics, '-' and '.'\n"), name);
+ return 1;
+ }
+
+ g_variant_builder_init (¶ms, G_VARIANT_TYPE ("av"));
+
+ if (args[2])
+ {
+ GError *error = NULL;
+ GVariant *parameter;
+
+ parameter = g_variant_parse (NULL, args[2], NULL, NULL, &error);
+
+ if (!parameter)
+ {
+ g_printerr (_("error parsing action parameter: %s\n"), error->message);
+ g_variant_builder_clear (¶ms);
+ g_error_free (error);
+ return 1;
+ }
+
+ g_variant_builder_add (¶ms, "v", parameter);
+ g_variant_unref (parameter);
+
+ if (args[3])
+ {
+ g_printerr (_("actions accept a maximum of one parameter\n"));
+ g_variant_builder_clear (¶ms);
+ return 1;
+ }
+ }
+
+ return app_call (args[0], "ActivateAction", g_variant_new ("(sav@a{sv})", name, ¶ms, app_get_platform_data ()));
+}
+
+static int
+app_activate (const gchar *app_id)
+{
+ return app_call (app_id, "Activate", g_variant_new ("(@a{sv})", app_get_platform_data ()));
+}
+
+static int
+app_launch (gchar **args)
+{
+ GVariantBuilder files;
+ gint i;
+
+ if (!app_check_name (args, "launch"))
+ return 1;
+
+ if (args[1] == NULL)
+ return app_activate (args[0]);
+
+ g_variant_builder_init (&files, G_VARIANT_TYPE_STRING_ARRAY);
+
+ for (i = 1; args[i]; i++)
+ {
+ GFile *file;
+
+ /* "This operation never fails" */
+ file = g_file_new_for_commandline_arg (args[i]);
+ g_variant_builder_add_value (&files, g_variant_new_take_string (g_file_get_uri (file)));
+ g_object_unref (file);
+ }
+
+ return app_call (args[0], "Open", g_variant_new ("(as@a{sv})", &files, app_get_platform_data ()));
+}
+
+static int
+app_list_actions (gchar **args)
+{
+ const gchar * const *actions;
+ GDesktopAppInfo *app_info;
+ gchar *filename;
+ gint i;
+
+ if (!app_check_name (args, "list-actions"))
+ return 1;
+
+ if (args[1])
+ {
+ g_printerr (_("list-actions command takes only the application id"));
+ app_help (FALSE, "list-actions");
+ }
+
+ filename = g_strconcat (args[0], ".desktop", NULL);
+ app_info = g_desktop_app_info_new (filename);
+ g_free (filename);
+
+ if (app_info == NULL)
+ {
+ g_printerr (_("unable to find desktop file for application %s\n"), args[0]);
+ return 1;
+ }
+
+ actions = g_desktop_app_info_list_actions (app_info);
+
+ for (i = 0; actions[i]; i++)
+ g_print ("%s\n", actions[i]);
+
+ g_object_unref (app_info);
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+ textdomain (GETTEXT_PACKAGE);
+ bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR);
+#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif
+
+ if (argc < 2)
+ return app_help (TRUE, NULL);
+
+ if (g_str_equal (argv[1], "help"))
+ return app_help (TRUE, argv[2]);
+
+ if (g_str_equal (argv[1], "version"))
+ return app_version (argv + 2);
+
+ if (g_str_equal (argv[1], "list-apps"))
+ return app_list (argv + 2);
+
+ if (g_str_equal (argv[1], "launch"))
+ return app_launch (argv + 2);
+
+ if (g_str_equal (argv[1], "action"))
+ return app_action (argv + 2);
+
+ if (g_str_equal (argv[1], "list-actions"))
+ return app_list_actions (argv + 2);
+
+ g_printerr (_("unrecognised command: %s\n\n"), argv[1]);
+
+ return app_help (FALSE, NULL);
+}