mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-11-05 02:28:55 +01:00
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
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*
|
*
|
||||||
* Author: Alexander Larsson <alexl@redhat.com>
|
* Author: Alexander Larsson <alexl@redhat.com>
|
||||||
|
* Ryan Lortie <desrt@desrt.ca>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -94,6 +95,7 @@ struct _GDesktopAppInfo
|
|||||||
|
|
||||||
char *desktop_id;
|
char *desktop_id;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
char *app_id;
|
||||||
|
|
||||||
GKeyFile *keyfile;
|
GKeyFile *keyfile;
|
||||||
|
|
||||||
@@ -197,6 +199,7 @@ g_desktop_app_info_finalize (GObject *object)
|
|||||||
g_free (info->categories);
|
g_free (info->categories);
|
||||||
g_free (info->startup_wm_class);
|
g_free (info->startup_wm_class);
|
||||||
g_strfreev (info->mime_types);
|
g_strfreev (info->mime_types);
|
||||||
|
g_free (info->app_id);
|
||||||
|
|
||||||
G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
|
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 *type;
|
||||||
char *try_exec;
|
char *try_exec;
|
||||||
char *exec;
|
char *exec;
|
||||||
|
gboolean bus_activatable;
|
||||||
|
|
||||||
start_group = g_key_file_get_start_group (key_file);
|
start_group = g_key_file_get_start_group (key_file);
|
||||||
if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
|
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->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->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);
|
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;
|
info->icon = NULL;
|
||||||
if (info->icon_name)
|
if (info->icon_name)
|
||||||
@@ -411,6 +416,25 @@ g_desktop_app_info_load_from_keyfile (GDesktopAppInfo *info,
|
|||||||
info->path = NULL;
|
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);
|
info->keyfile = g_key_file_ref (key_file);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
@@ -590,6 +614,7 @@ g_desktop_app_info_dup (GAppInfo *appinfo)
|
|||||||
new_info->exec = g_strdup (info->exec);
|
new_info->exec = g_strdup (info->exec);
|
||||||
new_info->binary = g_strdup (info->binary);
|
new_info->binary = g_strdup (info->binary);
|
||||||
new_info->path = g_strdup (info->path);
|
new_info->path = g_strdup (info->path);
|
||||||
|
new_info->app_id = g_strdup (info->app_id);
|
||||||
new_info->hidden = info->hidden;
|
new_info->hidden = info->hidden;
|
||||||
new_info->terminal = info->terminal;
|
new_info->terminal = info->terminal;
|
||||||
new_info->startup_notify = info->startup_notify;
|
new_info->startup_notify = info->startup_notify;
|
||||||
@@ -1276,7 +1301,8 @@ notify_desktop_launch (GDBusConnection *session_bus,
|
|||||||
#define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
|
#define _SPAWN_FLAGS_DEFAULT (G_SPAWN_SEARCH_PATH)
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
_g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
|
g_desktop_app_info_launch_uris_with_spawn (GDesktopAppInfo *info,
|
||||||
|
GDBusConnection *session_bus,
|
||||||
GList *uris,
|
GList *uris,
|
||||||
GAppLaunchContext *launch_context,
|
GAppLaunchContext *launch_context,
|
||||||
GSpawnFlags spawn_flags,
|
GSpawnFlags spawn_flags,
|
||||||
@@ -1286,20 +1312,16 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
|
|||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
|
|
||||||
GDBusConnection *session_bus;
|
|
||||||
gboolean completed = FALSE;
|
gboolean completed = FALSE;
|
||||||
GList *old_uris;
|
GList *old_uris;
|
||||||
char **argv, **envp;
|
char **argv, **envp;
|
||||||
int argc;
|
int argc;
|
||||||
ChildSetupData data;
|
ChildSetupData data;
|
||||||
|
|
||||||
g_return_val_if_fail (appinfo != NULL, FALSE);
|
g_return_val_if_fail (info != NULL, FALSE);
|
||||||
|
|
||||||
argv = NULL;
|
argv = NULL;
|
||||||
|
|
||||||
session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
|
||||||
|
|
||||||
if (launch_context)
|
if (launch_context)
|
||||||
envp = g_app_launch_context_get_environment (launch_context);
|
envp = g_app_launch_context_get_environment (launch_context);
|
||||||
else
|
else
|
||||||
@@ -1357,7 +1379,7 @@ _g_desktop_app_info_launch_uris_internal (GAppInfo *appinfo,
|
|||||||
GList *launched_files = create_files_for_uris (launched_uris);
|
GList *launched_files = create_files_for_uris (launched_uris);
|
||||||
|
|
||||||
display = g_app_launch_context_get_display (launch_context,
|
display = g_app_launch_context_get_display (launch_context,
|
||||||
appinfo,
|
G_APP_INFO (info),
|
||||||
launched_files);
|
launched_files);
|
||||||
if (display)
|
if (display)
|
||||||
envp = g_environ_setenv (envp, "DISPLAY", display, TRUE);
|
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)
|
if (info->startup_notify)
|
||||||
{
|
{
|
||||||
sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
|
sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
|
||||||
appinfo,
|
G_APP_INFO (info),
|
||||||
launched_files);
|
launched_files);
|
||||||
envp = g_environ_setenv (envp, "DESKTOP_STARTUP_ID", sn_id, TRUE);
|
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);
|
while (uris != NULL);
|
||||||
|
|
||||||
/* TODO - need to handle the process exiting immediately
|
completed = TRUE;
|
||||||
* after launching an app. See http://bugzilla.gnome.org/606960
|
|
||||||
|
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)
|
if (session_bus != NULL)
|
||||||
{
|
{
|
||||||
/* This asynchronous flush holds a reference until it completes,
|
/* 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.
|
* the connection if we were the initial owner.
|
||||||
*/
|
*/
|
||||||
g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
|
g_dbus_connection_flush (session_bus, NULL, NULL, NULL);
|
||||||
|
g_object_unref (session_bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
completed = TRUE;
|
return success;
|
||||||
|
|
||||||
out:
|
|
||||||
g_clear_object (&session_bus);
|
|
||||||
g_strfreev (argv);
|
|
||||||
g_strfreev (envp);
|
|
||||||
|
|
||||||
return completed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
@@ -1453,7 +1576,7 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
|
|||||||
GAppLaunchContext *launch_context,
|
GAppLaunchContext *launch_context,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
return _g_desktop_app_info_launch_uris_internal (appinfo, uris,
|
return g_desktop_app_info_launch_uris_internal (appinfo, uris,
|
||||||
launch_context,
|
launch_context,
|
||||||
_SPAWN_FLAGS_DEFAULT,
|
_SPAWN_FLAGS_DEFAULT,
|
||||||
NULL, NULL, NULL, NULL,
|
NULL, NULL, NULL, NULL,
|
||||||
@@ -1525,15 +1648,15 @@ g_desktop_app_info_launch (GAppInfo *appinfo,
|
|||||||
* launch applications. Ordinary applications should use
|
* launch applications. Ordinary applications should use
|
||||||
* g_app_info_launch_uris().
|
* g_app_info_launch_uris().
|
||||||
*
|
*
|
||||||
* In contrast to g_app_info_launch_uris(), all processes created will
|
* If the application is launched via traditional UNIX fork()/exec()
|
||||||
* always be run directly as children as if by the UNIX fork()/exec()
|
* then @spawn_flags, @user_setup and @user_setup_data are used for the
|
||||||
* calls.
|
* 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
|
* If application launching occurs via some other mechanism (eg: D-Bus
|
||||||
* of the child processes, which is provided via a setup function
|
* activation) then @spawn_flags, @user_setup, @user_setup_data,
|
||||||
* @user_setup, as well as the process identifier of each child process
|
* @pid_callback and @pid_callback_data are ignored.
|
||||||
* via @pid_callback. See g_spawn_async() for more information about the
|
|
||||||
* semantics of the @user_setup function.
|
|
||||||
*
|
*
|
||||||
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||||
*/
|
*/
|
||||||
@@ -1548,7 +1671,7 @@ g_desktop_app_info_launch_uris_as_manager (GDesktopAppInfo *appinfo,
|
|||||||
gpointer pid_callback_data,
|
gpointer pid_callback_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
return _g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
|
return g_desktop_app_info_launch_uris_internal ((GAppInfo*)appinfo,
|
||||||
uris,
|
uris,
|
||||||
launch_context,
|
launch_context,
|
||||||
spawn_flags,
|
spawn_flags,
|
||||||
|
|||||||
@@ -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_NOTIFY "StartupNotify"
|
||||||
#define G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS "StartupWMClass"
|
#define G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS "StartupWMClass"
|
||||||
#define G_KEY_FILE_DESKTOP_KEY_URL "URL"
|
#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_APPLICATION "Application"
|
||||||
#define G_KEY_FILE_DESKTOP_TYPE_LINK "Link"
|
#define G_KEY_FILE_DESKTOP_TYPE_LINK "Link"
|
||||||
|
|||||||
Reference in New Issue
Block a user