mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 00:12:19 +01:00 
			
		
		
		
	See https://developer.gnome.org/hig/stable/typography.html https://bugzilla.gnome.org/show_bug.cgi?id=772221
		
			
				
	
	
		
			470 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			470 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, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * 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 absolute 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] = '/';
 | |
|       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)
 | |
|         {
 | |
|           gchar *context;
 | |
| 
 | |
|           context = g_variant_parse_error_print_context (error, args[2]);
 | |
|           g_printerr (_("error parsing action parameter: %s\n"), context);
 | |
|           g_variant_builder_clear (¶ms);
 | |
|           g_error_free (error);
 | |
|           g_free (context);
 | |
|           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);
 | |
| }
 |