diff options
author | Ryan Lortie <desrt@desrt.ca> | 2013-04-29 13:37:55 -0700 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2013-06-07 18:36:55 -0400 |
commit | afc8b1020aaa3a8acd7e8328d148748d3a5adfe6 (patch) | |
tree | bd3a747d9f5153d81797118b2dffcf164fa9c712 | |
parent | 0aaac55d1474dde49a5ac664dda16f0f6deca9b6 (diff) | |
download | glib-afc8b1020aaa3a8acd7e8328d148748d3a5adfe6.tar.gz |
GDesktopAppInfo: support DBusActivatable
Support the sender-side of the freedesktop application specification for
cases that we find 'DBusActivatable=true' in the desktop file.
https://bugzilla.gnome.org/show_bug.cgi?id=699259
-rw-r--r-- | gio/gdesktopappinfo.c | 219 | ||||
-rw-r--r-- | glib/gkeyfile.h | 1 |
2 files changed, 172 insertions, 48 deletions
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 5fe3a9ac8..37bd45f9f 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -19,6 +19,7 @@ * Boston, MA 02111-1307, USA. * * Author: Alexander Larsson <alexl@redhat.com> + * Ryan Lortie <desrt@desrt.ca> */ #include "config.h" @@ -94,6 +95,7 @@ struct _GDesktopAppInfo char *desktop_id; char *filename; + char *app_id; GKeyFile *keyfile; @@ -197,6 +199,7 @@ g_desktop_app_info_finalize (GObject *object) g_free (info->categories); g_free (info->startup_wm_class); g_strfreev (info->mime_types); + g_free (info->app_id); G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object); } @@ -290,6 +293,7 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info, char *type; char *try_exec; char *exec; + gboolean bus_activatable; start_group = g_key_file_get_start_group (key_file); if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0) @@ -375,6 +379,7 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info, info->categories = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, NULL); info->startup_wm_class = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, STARTUP_WM_CLASS_KEY, NULL); info->mime_types = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL, NULL); + bus_activatable = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE, NULL); info->icon = NULL; if (info->icon_name) @@ -411,6 +416,25 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info, info->path = NULL; } + if (bus_activatable) + { + gchar *basename; + gchar *last_dot; + + basename = g_path_get_basename (info->filename); + last_dot = strrchr (basename, '.'); + + if (last_dot && g_str_equal (last_dot, ".desktop")) + { + *last_dot = '\0'; + + if (g_dbus_is_interface_name (basename)) + info->app_id = g_strdup (basename); + } + + g_free (basename); + } + info->keyfile = g_key_file_ref (key_file); return TRUE; @@ -590,6 +614,7 @@ g_desktop_app_info_dup (GAppInfo *appinfo) new_info->exec = g_strdup (info->exec); new_info->binary = g_strdup (info->binary); new_info->path = g_strdup (info->path); + new_info->app_id = g_strdup (info->app_id); new_info->hidden = info->hidden; new_info->terminal = info->terminal; new_info->startup_notify = info->startup_notify; @@ -1276,30 +1301,27 @@ notify_desktop_launch (GDBusConnection *session_bus, #define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH) static gboolean -_g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, - GList *uris, - GAppLaunchContext *launch_context, - GSpawnFlags spawn_flags, - GSpawnChildSetupFunc user_setup, - gpointer user_setup_data, - GDesktopAppLaunchCallback pid_callback, - gpointer pid_callback_data, - GError **error) +g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info, + GDBusConnection *session_bus, + GList *uris, + GAppLaunchContext *launch_context, + GSpawnFlags spawn_flags, + GSpawnChildSetupFunc user_setup, + gpointer user_setup_data, + GDesktopAppLaunchCallback pid_callback, + gpointer pid_callback_data, + GError **error) { - GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); - GDBusConnection *session_bus; gboolean completed = FALSE; GList *old_uris; char **argv, **envp; int argc; ChildSetupData data; - g_return_val_if_fail (appinfo != NULL, FALSE); + g_return_val_if_fail (info != NULL, FALSE); argv = NULL; - session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); - if (launch_context) envp = g_app_launch_context_get_environment (launch_context); else @@ -1357,7 +1379,7 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, GList *launched_files = create_files_for_uris (launched_uris); display = g_app_launch_context_get_display (launch_context, - appinfo, + G_APP_INFO (info), launched_files); if (display) envp = g_environ_setenv (envp, "DISPLAY", display, TRUE); @@ -1365,7 +1387,7 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, if (info->startup_notify) { sn_id = g_app_launch_context_get_startup_notify_id (launch_context, - appinfo, + G_APP_INFO (info), launched_files); envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE); } @@ -1425,9 +1447,116 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, } while (uris != NULL); - /* TODO - need to handle the process exiting immediately - * after launching an app. See http://bugzilla.gnome.org/606960 + completed = TRUE; + + out: + g_strfreev (argv); + g_strfreev (envp); + + return completed; +} + +static gchar * +object_path_from_appid (const gchar *app_id) +{ + gchar *path; + gint i, n; + + n = strlen (app_id); + path = g_malloc (n + 2); + + path[0] = '/'; + + for (i = 0; i < n; i++) + if (app_id[i] != '.') + path[i + 1] = app_id[i]; + else + path[i + 1] = '/'; + + path[i + 1] = '\0'; + + return path; +} + +static gboolean +g_desktop_app_info_launch_uris_with_dbus (GDesktopAppInfo *info, + GDBusConnection *session_bus, + GList *uris, + GAppLaunchContext *launch_context) +{ + GVariantBuilder builder; + gchar *object_path; + + g_return_val_if_fail (info != NULL, FALSE); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE); + + if (uris) + { + GList *iter; + + g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY); + for (iter = uris; iter; iter = iter->next) + g_variant_builder_add (&builder, "s", iter->data); + g_variant_builder_close (&builder); + } + + g_variant_builder_open (&builder, G_VARIANT_TYPE_VARDICT); + + if (launch_context) + { + GList *launched_files = create_files_for_uris (uris); + + if (info->startup_notify) + { + gchar *sn_id; + + sn_id = g_app_launch_context_get_startup_notify_id (launch_context, G_APP_INFO (info), launched_files); + g_variant_builder_add (&builder, "{sv}", "desktop-startup-id", g_variant_new_take_string (sn_id)); + } + + g_list_free_full (launched_files, g_object_unref); + } + + g_variant_builder_close (&builder); + + /* This is non-blocking API. Similar to launching via fork()/exec() + * we don't wait around to see if the program crashed during startup. + * This is what startup-notification's job is... */ + object_path = object_path_from_appid (info->app_id); + g_dbus_connection_call (session_bus, info->app_id, object_path, "org.freedesktop.Application", + uris ? "Open" : "Activate", g_variant_builder_end (&builder), + NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); + g_free (object_path); + + return TRUE; +} + +static gboolean +g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, + GList *uris, + GAppLaunchContext *launch_context, + GSpawnFlags spawn_flags, + GSpawnChildSetupFunc user_setup, + gpointer user_setup_data, + GDesktopAppLaunchCallback pid_callback, + gpointer pid_callback_data, + GError **error) +{ + GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo); + GDBusConnection *session_bus; + gboolean success = TRUE; + + session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); + + if (session_bus && info->app_id) + g_desktop_app_info_launch_uris_with_dbus (info, session_bus, uris, launch_context); + else + success = g_desktop_app_info_launch_uris_with_spawn (info, session_bus, uris, launch_context, + spawn_flags, user_setup, user_setup_data, + pid_callback, pid_callback_data, error); + if (session_bus != NULL) { /* This asynchronous flush holds a reference until it completes, @@ -1435,16 +1564,10 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo, * the connection if we were the initial owner. */ g_dbus_connection_flush (session_bus, NULL, NULL, NULL); + g_object_unref (session_bus); } - completed = TRUE; - - out: - g_clear_object (&session_bus); - g_strfreev (argv); - g_strfreev (envp); - - return completed; + return success; } static gboolean @@ -1453,11 +1576,11 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo, GAppLaunchContext *launch_context, GError **error) { - return _g_desktop_app_info_launch_uris_internal (appinfo, uris, - launch_context, - _SPAWN_FLAGS_DEFAULT, - NULL, NULL, NULL, NULL, - error); + return g_desktop_app_info_launch_uris_internal (appinfo, uris, + launch_context, + _SPAWN_FLAGS_DEFAULT, + NULL, NULL, NULL, NULL, + error); } static gboolean @@ -1525,15 +1648,15 @@ g_desktop_app_info_launch (GAppInfo *appinfo, * launch applications. Ordinary applications should use * g_app_info_launch_uris(). * - * In contrast to g_app_info_launch_uris(), all processes created will - * always be run directly as children as if by the UNIX fork()/exec() - * calls. + * If the application is launched via traditional UNIX fork()/exec() + * then @spawn_flags, @user_setup and @user_setup_data are used for the + * call to g_spawn_async(). Additionally, @pid_callback (with + * @pid_callback_data) will be called to inform about the PID of the + * created process. * - * This guarantee allows additional control over the exact environment - * of the child processes, which is provided via a setup function - * @user_setup, as well as the process identifier of each child process - * via @pid_callback. See g_spawn_async() for more information about the - * semantics of the @user_setup function. + * If application launching occurs via some other mechanism (eg: D-Bus + * activation) then @spawn_flags, @user_setup, @user_setup_data, + * @pid_callback and @pid_callback_data are ignored. * * Returns: %TRUE on successful launch, %FALSE otherwise. */ @@ -1548,15 +1671,15 @@ g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo, gpointer pid_callback_data, GError **error) { - return _g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo, - uris, - launch_context, - spawn_flags, - user_setup, - user_setup_data, - pid_callback, - pid_callback_data, - error); + return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo, + uris, + launch_context, + spawn_flags, + user_setup, + user_setup_data, + pid_callback, + pid_callback_data, + error); } /** diff --git a/glib/gkeyfile.h b/glib/gkeyfile.h index a4a1d3dfe..eaf0cce1f 100644 --- a/glib/gkeyfile.h +++ b/glib/gkeyfile.h @@ -305,6 +305,7 @@ gboolean g_key_file_remove_group (GKeyFile *key_file, #define G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY "StartupNotify" #define G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS "StartupWMClass" #define G_KEY_FILE_DESKTOP_KEY_URL "URL" +#define G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE "DBusActivatable" #define G_KEY_FILE_DESKTOP_TYPE_APPLICATION "Application" #define G_KEY_FILE_DESKTOP_TYPE_LINK "Link" |