mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-02 15:46:17 +01:00
9defb6b1b1
This is essentially a commandline implementation of the client-side of the org.freedesktop.Application D-Bus interface. It includes support for tab-completion based on desktop files and their contents. https://bugzilla.gnome.org/show_bug.cgi?id=704218
464 lines
12 KiB
C
464 lines
12 KiB
C
/*
|
|
* 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);
|
|
}
|