new GApplication implementation

This commit is contained in:
Ryan Lortie 2010-10-06 13:08:26 -04:00
parent 9d80c36141
commit a7923a4aa3
16 changed files with 1450 additions and 2724 deletions

View File

@ -142,10 +142,11 @@ application_headers = \
gapplication.h
application_sources = \
gactiongroup.c \
gsimpleactiongroup.c \
gaction.c \
gsimpleaction.c \
gactiongroup.c \
gsimpleactiongroup.c \
gaction.c \
gsimpleaction.c \
gapplicationimpl-dbus.c \
gapplication.c
local_sources = \

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2010 Red Hat, Inc
/*
* Copyright © 2010 Codethink Limited
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
@ -17,8 +16,7 @@
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* Authors: Colin Walters <walters@verbum.org>
* Emmanuele Bassi <ebassi@linux.intel.com>
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
@ -28,21 +26,22 @@
#ifndef __G_APPLICATION_H__
#define __G_APPLICATION_H__
#include <glib-object.h>
#include <gio/giotypes.h>
G_BEGIN_DECLS
#define G_TYPE_APPLICATION (g_application_get_type ())
#define G_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_APPLICATION, GApplication))
#define G_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_APPLICATION, GApplicationClass))
#define G_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APPLICATION))
#define G_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_APPLICATION))
#define G_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_APPLICATION, GApplicationClass))
#define G_TYPE_APPLICATION (g_application_get_type ())
#define G_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \
G_TYPE_APPLICATION, GApplication))
#define G_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_CAST ((class), \
G_TYPE_APPLICATION, GApplicationClass))
#define G_IS_APPLICATION(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_APPLICATION))
#define G_IS_APPLICATION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_APPLICATION))
#define G_APPLICATION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), \
G_TYPE_APPLICATION, GApplicationClass))
typedef struct _GApplication GApplication;
typedef struct _GApplicationPrivate GApplicationPrivate;
typedef struct _GApplicationClass GApplicationClass;
typedef struct _GApplicationPrivate GApplicationPrivate;
typedef struct _GApplicationClass GApplicationClass;
/**
* GApplication:
@ -62,13 +61,19 @@ struct _GApplication
/**
* GApplicationClass:
* @action_with_data: class handler for the #GApplication::action-with-data signal
* @quit_with_data: class handler for the #GApplication::quit-with-data signal
* @prepare_activation: class handler for the #GApplication::prepare-activation signal
* @run: virtual function, called by g_application_run()
*
* The <structname>GApplicationClass</structname> structure contains
* private data only
* @startup: invoked on the primary instance immediately after registration
* @activate: invoked on the primary instance when an activation occurs
* @open: invoked on the primary instance when there are files to open
* @local_command_line: invoked (locally) when the process has been invoked via commandline execution. The
* virtual function has the chance to inspect (and possibly replace) the list of command line arguments. See
* g_application_run() for more information.
* @before_emit: invoked on the primary instance before 'activate', 'open' or any action invocation
* @after_emit: invoked on the primary instance after 'activate', 'open' or any action invocation
* @add_platform_data: invoked (locally) to add 'platform data' to be sent to the primary instance when
* activating, opening or invoking actions
* @quit_mainloop: invoked on the primary instance when the use count of the application drops to zero (and
* after any inactivity timeout, if requested)
* @run_mainloop: invoked on the primary instance from g_application_run() if the use-count is non-zero
*
* Since: 2.26
*/
@ -79,70 +84,78 @@ struct _GApplicationClass
/*< public >*/
/* signals */
void (* action_with_data) (GApplication *application,
const gchar *action_name,
GVariant *platform_data);
gboolean (* quit_with_data) (GApplication *application,
GVariant *platform_data);
void (* prepare_activation) (GApplication *application,
GVariant *arguments,
GVariant *platform_data);
void (* startup) (GApplication *application);
void (* activate) (GApplication *application);
void (* open) (GApplication *application,
GFile **files,
gint n_files,
const gchar *hint);
gpointer _reserved_1;
/* vfuncs */
void (* run) (GApplication *application);
gboolean (* local_command_line) (GApplication *application,
GVariant **arguments,
int *exit_status);
gpointer _reserved_2;
void (* before_emit) (GApplication *application,
GVariant *platform_data);
void (* after_emit) (GApplication *application,
GVariant *platform_data);
void (* add_platform_data) (GApplication *application,
GVariantBuilder *builder);
void (* quit_mainloop) (GApplication *application);
void (* run_mainloop) (GApplication *application);
/*< private >*/
/* Padding for future expansion */
void (*_g_reserved1) (void);
void (*_g_reserved2) (void);
void (*_g_reserved3) (void);
void (*_g_reserved4) (void);
void (*_g_reserved5) (void);
void (*_g_reserved6) (void);
gpointer padding[12];
};
GType g_application_get_type (void) G_GNUC_CONST;
GApplication * g_application_new (const gchar *appid,
int argc,
char **argv);
GType g_application_get_type (void) G_GNUC_CONST;
GApplication * g_application_try_new (const gchar *appid,
int argc,
char **argv,
GError **error);
gboolean g_application_id_is_valid (const gchar *application_id);
GApplication * g_application_unregistered_try_new (const gchar *appid,
int argc,
char **argv,
GError **error);
GApplication * g_application_new (const gchar *application_id,
GApplicationFlags flags);
gboolean g_application_register (GApplication *application);
const gchar * g_application_get_application_id (GApplication *application);
void g_application_set_application_id (GApplication *application,
const gchar *application_id);
GApplication * g_application_get_instance (void);
G_CONST_RETURN gchar * g_application_get_id (GApplication *application);
guint g_application_get_inactivity_timeout (GApplication *application);
void g_application_set_inactivity_timeout (GApplication *application,
guint inactivity_timeout);
void g_application_add_action (GApplication *application,
const gchar *name,
const gchar *description);
void g_application_remove_action (GApplication *application,
const gchar *name);
gchar ** g_application_list_actions (GApplication *application);
void g_application_set_action_enabled (GApplication *application,
const gchar *name,
gboolean enabled);
gboolean g_application_get_action_enabled (GApplication *application,
const gchar *name);
G_CONST_RETURN gchar * g_application_get_action_description (GApplication *application,
const gchar *name);
void g_application_invoke_action (GApplication *application,
const gchar *name,
GVariant *platform_data);
GApplicationFlags g_application_get_flags (GApplication *application);
void g_application_set_flags (GApplication *application,
GApplicationFlags flags);
void g_application_run (GApplication *application);
gboolean g_application_quit_with_data (GApplication *application,
GVariant *platform_data);
gboolean g_application_get_is_registered (GApplication *application);
gboolean g_application_get_is_remote (GApplication *application);
gboolean g_application_is_remote (GApplication *application);
gboolean g_application_register (GApplication *application,
GCancellable *cancellable,
GError **error);
void g_application_hold (GApplication *application);
void g_application_release (GApplication *application);
void g_application_activate (GApplication *application);
void g_application_open (GApplication *application,
GFile **file,
gint n_files,
const gchar *hint);
int g_application_run (GApplication *application,
int argc,
char **argv);
int g_application_run_with_arguments (GApplication *application,
GVariant *arguments);
G_END_DECLS

View File

@ -0,0 +1,59 @@
/*
* Copyright © 2010 Codethink 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>
*/
static const GDBusArgInfo platform_data_arg = { -1, (gchar *) "platform_data", (gchar *) "a{sv}" };
static const GDBusArgInfo open_uris_arg = { -1, (gchar *) "uris", (gchar *) "as" };
static const GDBusArgInfo open_hint_arg = { -1, (gchar *) "hint", (gchar *) "s" };
static const GDBusArgInfo invoke_action_name_arg = { -1, (gchar *) "name", (gchar *) "s" };
static const GDBusArgInfo invoke_action_args_arg = { -1, (gchar *) "args", (gchar *) "v" };
static const GDBusArgInfo cmdline_path_arg = { -1, (gchar *) "path", (gchar *) "o" };
static const GDBusArgInfo cmdline_arguments_arg = { -1, (gchar *) "arguments", (gchar *) "aay" };
static const GDBusArgInfo cmdline_exit_status_arg = { -1, (gchar *) "exit_status", (gchar *) "i" };
static const GDBusArgInfo *activate_in[] = { &platform_data_arg, NULL };
static const GDBusArgInfo *activate_out[] = { NULL };
static const GDBusArgInfo *open_in[] = { &open_uris_arg, &open_hint_arg, &platform_data_arg, NULL };
static const GDBusArgInfo *open_out[] = { NULL };
static const GDBusMethodInfo activate_method = {
-1, (gchar *) "Activate",
(GDBusArgInfo **) activate_in,
(GDBusArgInfo **) activate_out
};
static const GDBusMethodInfo open_method = {
-1, (gchar *) "Open",
(GDBusArgInfo **) open_in,
(GDBusArgInfo **) open_out
};
static const GDBusMethodInfo *application_methods[] = {
&activate_method, &open_method, NULL
};
const GDBusInterfaceInfo org_gtk_Application = {
-1, (gchar *) "org.gtk.Application",
(GDBusMethodInfo **) application_methods
};

304
gio/gapplicationimpl-dbus.c Normal file
View File

@ -0,0 +1,304 @@
/*
* Copyright © 2010 Codethink 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 License, 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.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
*/
#include "gapplicationimpl.h"
#include "gapplication.h"
#include "gfile.h"
#include "gdbusconnection.h"
#include "gdbusintrospection.h"
#include "gdbuserror.h"
#include <string.h>
#include "gapplicationimpl-dbus-interface.c"
struct _GApplicationImpl
{
GDBusConnection *session_bus;
const gchar *bus_name;
gchar *object_path;
guint object_id;
gpointer app;
GMainLoop *cmdline_mainloop;
};
static void
g_application_impl_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
GApplicationImpl *impl = user_data;
GApplicationClass *class;
class = G_APPLICATION_GET_CLASS (impl->app);
if (strcmp (method_name, "Activate") == 0)
{
GVariant *platform_data;
g_variant_get (parameters, "(@a{sv})", &platform_data);
class->before_emit (impl->app, platform_data);
g_signal_emit_by_name (impl->app, "activate");
class->after_emit (impl->app, platform_data);
g_variant_unref (platform_data);
}
else if (strcmp (method_name, "Open") == 0)
{
GVariant *platform_data;
const gchar *hint;
GVariant *array;
GFile **files;
gint n, i;
g_variant_get (parameters, "(@ass@a{sv})",
&array, &hint, &platform_data);
n = g_variant_n_children (array);
files = g_new (GFile *, n + 1);
for (i = 0; i < n; i++)
{
const gchar *uri;
g_variant_get_child (array, i, "&s", &uri);
files[i] = g_file_new_for_uri (uri);
}
g_variant_unref (array);
files[n] = NULL;
class->before_emit (impl->app, platform_data);
g_signal_emit_by_name (impl->app, "open", files, n, hint);
class->after_emit (impl->app, platform_data);
g_variant_unref (platform_data);
for (i = 0; i < n; i++)
g_object_unref (files[i]);
g_free (files);
}
else
g_assert_not_reached ();
}
static gchar *
application_path_from_appid (const gchar *appid)
{
gchar *appid_path, *iter;
appid_path = g_strconcat ("/", appid, NULL);
for (iter = appid_path; *iter; iter++)
{
if (*iter == '.')
*iter = '/';
}
return appid_path;
}
void
g_application_impl_destroy (GApplicationImpl *impl)
{
if (impl->session_bus)
{
if (impl->object_id)
g_dbus_connection_unregister_object (impl->session_bus,
impl->object_id);
g_object_unref (impl->session_bus);
g_free (impl->object_path);
}
else
{
g_assert (impl->object_path == NULL);
g_assert (impl->object_id == 0);
}
g_slice_free (GApplicationImpl, impl);
}
GApplicationImpl *
g_application_impl_register (GApplication *application,
const gchar *appid,
GApplicationFlags flags,
gboolean *is_remote,
GCancellable *cancellable,
GError **error)
{
const static GDBusInterfaceVTable vtable = {
g_application_impl_method_call
};
GApplicationImpl *impl;
GVariant *reply;
guint32 rval;
impl = g_slice_new (GApplicationImpl);
impl->app = application;
impl->bus_name = appid;
impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION,
cancellable, error);
if (impl->session_bus == NULL)
{
g_slice_free (GApplicationImpl, impl);
return NULL;
}
impl->object_path = application_path_from_appid (appid);
if (flags & G_APPLICATION_FLAGS_IS_LAUNCHER)
{
impl->object_id = 0;
*is_remote = TRUE;
return impl;
}
impl->object_id = g_dbus_connection_register_object (impl->session_bus,
impl->object_path,
(GDBusInterfaceInfo *)
&org_gtk_Application,
&vtable,
impl, NULL,
error);
if (impl->object_id == 0)
{
g_object_unref (impl->session_bus);
g_free (impl->object_path);
impl->session_bus = NULL;
impl->object_path = NULL;
g_slice_free (GApplicationImpl, impl);
return NULL;
}
reply = g_dbus_connection_call_sync (impl->session_bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RequestName",
g_variant_new ("(su)",
/* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
impl->bus_name, 0x4),
G_VARIANT_TYPE ("(u)"),
0, -1, cancellable, error);
if (reply == NULL)
{
g_dbus_connection_unregister_object (impl->session_bus,
impl->object_id);
impl->object_id = 0;
g_object_unref (impl->session_bus);
g_free (impl->object_path);
impl->session_bus = NULL;
impl->object_path = NULL;
g_slice_free (GApplicationImpl, impl);
return NULL;
}
g_variant_get (reply, "(u)", &rval);
g_variant_unref (reply);
/* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
if ((*is_remote = (rval == 3)))
{
g_dbus_connection_unregister_object (impl->session_bus,
impl->object_id);
impl->object_id = 0;
if (flags & G_APPLICATION_FLAGS_IS_SERVICE)
{
g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
"Unable to acquire bus name `%s'", appid);
g_object_unref (impl->session_bus);
g_free (impl->object_path);
g_slice_free (GApplicationImpl, impl);
impl = NULL;
}
}
return impl;
}
void
g_application_impl_activate (GApplicationImpl *impl,
GVariant *platform_data)
{
g_dbus_connection_call (impl->session_bus,
impl->bus_name,
impl->object_path,
"org.gtk.Application",
"Activate",
g_variant_new ("(@a{sv})", platform_data),
NULL, 0, -1, NULL, NULL, NULL);
}
void
g_application_impl_open (GApplicationImpl *impl,
GFile **files,
gint n_files,
const gchar *hint,
GVariant *platform_data)
{
GVariantBuilder builder;
gint i;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
for (i = 0; i < n_files; i++)
{
gchar *uri = g_file_get_uri (files[i]);
g_variant_builder_add (&builder, "s", uri);
g_free (uri);
}
g_variant_builder_close (&builder);
g_variant_builder_add (&builder, "s", hint);
g_variant_builder_add_value (&builder, platform_data);
g_dbus_connection_call (impl->session_bus,
impl->bus_name,
impl->object_path,
"org.gtk.Application",
"Open",
g_variant_builder_end (&builder),
NULL, 0, -1, NULL, NULL, NULL);
}
void
g_application_impl_flush (GApplicationImpl *impl)
{
g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
}

28
gio/gapplicationimpl.h Normal file
View File

@ -0,0 +1,28 @@
#include "giotypes.h"
typedef struct _GApplicationImpl GApplicationImpl;
G_GNUC_INTERNAL
void g_application_impl_destroy (GApplicationImpl *impl);
G_GNUC_INTERNAL
GApplicationImpl * g_application_impl_register (GApplication *application,
const gchar *appid,
GApplicationFlags flags,
gboolean *is_remote,
GCancellable *cancellable,
GError **error);
G_GNUC_INTERNAL
void g_application_impl_activate (GApplicationImpl *impl,
GVariant *platform_data);
G_GNUC_INTERNAL
void g_application_impl_open (GApplicationImpl *impl,
GFile **files,
gint n_files,
const gchar *hint,
GVariant *platform_data);
G_GNUC_INTERNAL
void g_application_impl_flush (GApplicationImpl *impl);

View File

@ -1,424 +0,0 @@
/* GIO - GLib Input, Output and Streaming Library
*
* Copyright © 2010 Red Hat, Inc
*
* 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 License, 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.
*
* Authors: Colin Walters <walters@verbum.org>
*/
#define G_APPLICATION_IFACE "org.gtk.Application"
static void
application_dbus_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
GApplication *app = G_APPLICATION (user_data);
if (method_name == NULL && *method_name == '\0')
return;
if (strcmp (method_name, "Quit") == 0)
{
GVariant *platform_data;
g_variant_get (parameters, "(@a{sv})", &platform_data);
g_dbus_method_invocation_return_value (invocation, NULL);
g_application_quit_with_data (app, platform_data);
g_variant_unref (platform_data);
}
else if (strcmp (method_name, "ListActions") == 0)
{
GHashTableIter iter;
GApplicationAction *value;
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{s(sb)})"));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{s(sb)}"));
g_hash_table_iter_init (&iter, app->priv->actions);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&value))
g_variant_builder_add (&builder, "{s(sb)}",
value->name,
value->description ? value->description : "",
value->enabled);
g_variant_builder_close (&builder);
g_dbus_method_invocation_return_value (invocation,
g_variant_builder_end (&builder));
}
else if (strcmp (method_name, "InvokeAction") == 0)
{
const char *action_name;
GVariant *platform_data;
GApplicationAction *action;
g_variant_get (parameters, "(&s@a{sv})", &action_name, &platform_data);
action = g_hash_table_lookup (app->priv->actions, action_name);
if (!action)
{
char *errmsg = g_strdup_printf ("Invalid action: %s", action_name);
g_dbus_method_invocation_return_dbus_error (invocation, G_APPLICATION_IFACE ".InvalidAction", errmsg);
g_free (errmsg);
g_variant_unref (platform_data);
return;
}
g_signal_emit (app, application_signals[ACTION_WITH_DATA],
g_quark_from_string (action_name), action_name, platform_data);
g_dbus_method_invocation_return_value (invocation, NULL);
g_variant_unref (platform_data);
}
else if (strcmp (method_name, "Activate") == 0)
{
GVariant *args;
GVariant *platform_data;
g_variant_get (parameters, "(@aay@a{sv})", &args, &platform_data);
g_signal_emit (app, application_signals[PREPARE_ACTIVATION], 0, args, platform_data);
g_variant_unref (args);
g_variant_unref (platform_data);
g_dbus_method_invocation_return_value (invocation, NULL);
}
}
static const GDBusArgInfo application_quit_in_args[] =
{
{
-1,
"platform_data",
"a{sv}",
NULL
}
};
static const GDBusArgInfo * const application_quit_in_args_p[] = {
&application_quit_in_args[0],
NULL
};
static const GDBusArgInfo application_list_actions_out_args[] =
{
{
-1,
"actions",
"a{s(sb)}",
NULL
}
};
static const GDBusArgInfo * const application_list_actions_out_args_p[] = {
&application_list_actions_out_args[0],
NULL
};
static const GDBusArgInfo application_invoke_action_in_args[] =
{
{
-1,
"action",
"s",
NULL
},
{
-1,
"platform_data",
"a{sv}",
NULL
}
};
static const GDBusArgInfo * const application_invoke_action_in_args_p[] = {
&application_invoke_action_in_args[0],
&application_invoke_action_in_args[1],
NULL
};
static const GDBusMethodInfo application_quit_method_info =
{
-1,
"Quit",
(GDBusArgInfo **) &application_quit_in_args_p,
NULL,
NULL
};
static const GDBusMethodInfo application_list_actions_method_info =
{
-1,
"ListActions",
NULL,
(GDBusArgInfo **) &application_list_actions_out_args_p,
NULL
};
static const GDBusMethodInfo application_invoke_action_method_info =
{
-1,
"InvokeAction",
(GDBusArgInfo **) &application_invoke_action_in_args_p,
NULL,
NULL
};
static const GDBusArgInfo application_activate_in_args[] =
{
{
-1,
"arguments",
"aay",
NULL
},
{
-1,
"data",
"a{sv}",
NULL
}
};
static const GDBusArgInfo * const application_activate_in_args_p[] = {
&application_activate_in_args[0],
&application_activate_in_args[1],
NULL
};
static const GDBusMethodInfo application_activate_method_info =
{
-1,
"Activate",
(GDBusArgInfo **) &application_activate_in_args_p,
NULL,
NULL
};
static const GDBusMethodInfo * const application_dbus_method_info_p[] =
{
&application_quit_method_info,
&application_list_actions_method_info,
&application_invoke_action_method_info,
&application_activate_method_info,
NULL
};
static const GDBusSignalInfo application_dbus_signal_info[] =
{
{
-1,
"ActionsChanged",
NULL,
NULL
}
};
static const GDBusSignalInfo * const application_dbus_signal_info_p[] = {
&application_dbus_signal_info[0],
NULL
};
static const GDBusInterfaceInfo application_dbus_interface_info =
{
-1,
G_APPLICATION_IFACE,
(GDBusMethodInfo **) application_dbus_method_info_p,
(GDBusSignalInfo **) application_dbus_signal_info_p,
NULL,
};
static GDBusInterfaceVTable application_dbus_vtable =
{
application_dbus_method_call,
NULL,
NULL
};
static gchar *
application_path_from_appid (const gchar *appid)
{
gchar *appid_path, *iter;
appid_path = g_strconcat ("/", appid, NULL);
for (iter = appid_path; *iter; iter++)
{
if (*iter == '.')
*iter = '/';
}
return appid_path;
}
static gboolean
_g_application_platform_init (GApplication *app,
GCancellable *cancellable,
GError **error)
{
if (app->priv->session_bus == NULL)
app->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, error);
if (!app->priv->session_bus)
return FALSE;
if (!app->priv->dbus_path)
app->priv->dbus_path = application_path_from_appid (app->priv->appid);
return TRUE;
}
static gboolean
_g_application_platform_register (GApplication *app,
gboolean *unique,
GCancellable *cancellable,
GError **error)
{
GVariant *request_result;
guint32 request_status;
gboolean result;
guint registration_id;
/* Callers should have verified this */
g_assert (app->priv->registration_tried == FALSE);
app->priv->registration_tried = TRUE;
registration_id = g_dbus_connection_register_object (app->priv->session_bus,
app->priv->dbus_path,
(GDBusInterfaceInfo *) &application_dbus_interface_info,
&application_dbus_vtable,
app,
NULL,
error);
if (registration_id == 0)
return FALSE;
request_result = g_dbus_connection_call_sync (app->priv->session_bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"RequestName",
g_variant_new ("(su)", app->priv->appid, 0x4),
NULL, 0, -1, cancellable, error);
if (request_result == NULL)
{
result = FALSE;
goto done;
}
if (g_variant_is_of_type (request_result, G_VARIANT_TYPE ("(u)")))
g_variant_get (request_result, "(u)", &request_status);
else
request_status = 0;
g_variant_unref (request_result);
*unique = (request_status == 1 || request_status == 4);
result = TRUE;
if (*unique)
{
app->priv->is_remote = FALSE;
}
else
{
GVariantBuilder builder;
GVariant *message;
GVariant *result;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("(aaya{sv})"));
g_variant_builder_add_value (&builder, app->priv->argv);
g_variant_builder_add_value (&builder, app->priv->platform_data);
message = g_variant_builder_end (&builder);
result = g_dbus_connection_call_sync (app->priv->session_bus,
app->priv->appid,
app->priv->dbus_path,
G_APPLICATION_IFACE,
"Activate",
message,
NULL, 0, -1, NULL, NULL);
if (result)
g_variant_unref (result);
if (app->priv->default_quit)
exit (0);
}
done:
if (!result)
g_dbus_connection_unregister_object (app->priv->session_bus, registration_id);
return result;
}
static void
_g_application_platform_on_actions_changed (GApplication *app)
{
g_dbus_connection_emit_signal (app->priv->session_bus, NULL,
app->priv->dbus_path,
G_APPLICATION_IFACE,
"ActionsChanged", NULL, NULL);
}
static void
_g_application_platform_remote_invoke_action (GApplication *app,
const gchar *action,
GVariant *platform_data)
{
GVariant *result;
result = g_dbus_connection_call_sync (app->priv->session_bus,
app->priv->appid,
app->priv->dbus_path,
G_APPLICATION_IFACE,
"InvokeAction",
g_variant_new ("(s@a{sv})",
action,
platform_data),
NULL, 0, -1, NULL, NULL);
if (result)
g_variant_unref (result);
}
static void
_g_application_platform_remote_quit (GApplication *app,
GVariant *platform_data)
{
GVariant *result;
result = g_dbus_connection_call_sync (app->priv->session_bus,
app->priv->appid,
app->priv->dbus_path,
G_APPLICATION_IFACE,
"Quit",
g_variant_new ("(@a{sv})",
platform_data),
NULL, 0, -1, NULL, NULL);
if (result)
g_variant_unref (result);
}

View File

@ -21,4 +21,5 @@ VOID:STRING,STRING,VARIANT
VOID:STRING
VOID:STRING,STRING
VOID:STRING,BOOLEAN
BOOL:OBJECT
VOID:POINTER,INT,STRING
BOOLEAN:OBJECT

View File

@ -27,22 +27,24 @@ g_vfs_get_local
#if IN_HEADER(__G_APPLICATION_H__)
#if IN_FILE(__G_APPLICATION_C__)
g_application_get_type G_GNUC_CONST
g_application_activate
g_application_flags_get_type
g_application_get_application_id
g_application_get_flags
g_application_get_inactivity_timeout
g_application_get_is_registered
g_application_get_is_remote
g_application_hold
g_application_id_is_valid
g_application_new
g_application_get_instance
g_application_get_id
g_application_set_action_enabled
g_application_get_action_enabled
g_application_get_action_description
g_application_add_action
g_application_remove_action
g_application_invoke_action
g_application_list_actions
g_application_run
g_application_open
g_application_register
g_application_try_new
g_application_unregistered_try_new
g_application_quit_with_data
g_application_is_remote
g_application_release
g_application_run
g_application_run_with_arguments
g_application_set_application_id
g_application_set_flags
g_application_set_inactivity_timeout
#endif
#endif

View File

@ -1216,6 +1216,24 @@ typedef enum
G_DBUS_MESSAGE_BYTE_ORDER_LITTLE_ENDIAN = 'l'
} GDBusMessageByteOrder;
/**
* GApplicationFlags:
* @G_APPLICATION_FLAGS_NONE: Default
* @G_APPLICATION_FLAGS_HANDLE_OPEN: This application handles opening files.
*
* Flags used to define the behaviour of a #GApplication.
*
* Since: 2.26
**/
typedef enum
{
G_APPLICATION_FLAGS_NONE,
G_APPLICATION_FLAGS_IS_SERVICE = (1 << 0),
G_APPLICATION_FLAGS_IS_LAUNCHER = (1 << 1),
G_APPLICATION_FLAGS_HANDLES_OPEN = (1 << 2)
} GApplicationFlags;
G_END_DECLS
#endif /* __GIO_ENUMS_H__ */

View File

@ -51,6 +51,8 @@ typedef struct _GSimpleActionGroup GSimpleActionGroup;
typedef struct _GActionGroup GActionGroup;
typedef struct _GSimpleAction GSimpleAction;
typedef struct _GAction GAction;
typedef struct _GApplication GApplication;
typedef struct _GApplicationCommandLine GApplicationCommandLine;
typedef struct _GSettingsBackend GSettingsBackend;
typedef struct _GSettings GSettings;
typedef struct _GPermission GPermission;

View File

@ -1,7 +1,6 @@
actions
appinfo
appinfo-test
application
async-close-output-stream
buffered-input-stream
buffered-output-stream
@ -65,8 +64,6 @@ sleepy-stream
socket-client
socket-server
srvtarget
testapp
testapps
test.mo
unix-fd
unix-streams

View File

@ -59,8 +59,6 @@ TEST_PROGS += \
gdbus-exit-on-close \
gdbus-non-socket \
gdbus-bz627724 \
application \
testapps \
appinfo \
contenttype \
file \
@ -84,7 +82,6 @@ SAMPLE_PROGS = \
gdbus-example-peer \
gdbus-example-proxy-subclass \
gdbus-connection-flush-helper \
testapp \
appinfo-test \
proxy \
$(NULL)
@ -291,21 +288,12 @@ gdbus_example_export_LDADD = $(progs_ldadd)
gdbus_connection_flush_helper_SOURCES = gdbus-connection-flush-helper.c
gdbus_connection_flush_helper_LDADD = $(progs_ldadd)
application_SOURCES = application.c gdbus-sessionbus.c gdbus-sessionbus.h
application_LDADD = $(progs_ldadd)
appinfo_SOURCES = appinfo.c
appinfo_LDADD = $(progs_ldadd)
appinfo_test_SOURCES = appinfo-test.c
appinfo_test_LDADD = $(progs_ldadd)
testapp_SOURCES = testapp.c
testapp_LDADD = $(progs_ldadd)
testapps_SOURCES = testapps.c gdbus-sessionbus.c gdbus-sessionbus.h
testapps_LDADD = $(progs_ldadd)
contenttype_SOURCES = contenttype.c
contenttype_LDADD = $(progs_ldadd)

View File

@ -1,222 +0,0 @@
#include <stdlib.h>
#include <gio/gio.h>
#include <gstdio.h>
#include "gdbus-sessionbus.h"
enum
{
INVOKE_ACTION,
CHECK_ACTION,
DISABLE_ACTION,
INVOKE_DISABLED_ACTION,
CHECK_DISABLED_ACTION,
END
};
static guint timestamp = 0;
static gint state = -1;
static gboolean action_invoked = FALSE;
static void
on_app_action (GApplication *application,
const gchar *action_name,
GVariant *platform_data)
{
gboolean found_timestamp;
GVariantIter *iter;
const char *key;
guint action_timestamp;
GVariant *value;
if (g_test_verbose ())
{
char *str = g_variant_print (platform_data, FALSE);
g_print ("Action '%s' invoked (data: %s, expected: %u)\n",
action_name,
str,
timestamp);
g_free (str);
}
g_assert_cmpstr (action_name, ==, "About");
g_variant_get (platform_data, "a{sv}", &iter);
found_timestamp = FALSE;
while (g_variant_iter_next (iter, "{&sv}",
&key, &value))
{
if (g_strcmp0 ("timestamp", key) == 0)
{
found_timestamp = TRUE;
g_variant_get (value, "u", &action_timestamp);
break;
}
}
g_variant_iter_free (iter);
g_assert_cmpuint (timestamp, ==, action_timestamp);
action_invoked = TRUE;
}
static GVariant *
create_timestamp_data ()
{
GVariantBuilder builder;
timestamp = 42 + timestamp;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
g_variant_builder_add (&builder, "{sv}",
"timestamp", g_variant_new ("u", timestamp));
return g_variant_builder_end (&builder);
}
static gboolean
check_invoke_action (gpointer data)
{
GApplication *application = data;
if (state == INVOKE_ACTION)
{
if (g_test_verbose ())
g_print ("Invoking About...\n");
g_application_invoke_action (application, "About", create_timestamp_data ());
state = CHECK_ACTION;
return TRUE;
}
if (state == CHECK_ACTION)
{
if (g_test_verbose ())
g_print ("Verifying About invocation...\n");
g_assert (action_invoked);
state = DISABLE_ACTION;
return TRUE;
}
if (state == DISABLE_ACTION)
{
if (g_test_verbose ())
g_print ("Disabling About...\n");
g_application_set_action_enabled (application, "About", FALSE);
action_invoked = FALSE;
state = INVOKE_DISABLED_ACTION;
return TRUE;
}
if (state == INVOKE_DISABLED_ACTION)
{
if (g_test_verbose ())
g_print ("Invoking disabled About action...\n");
g_application_invoke_action (application, "About", create_timestamp_data ());
state = CHECK_DISABLED_ACTION;
return TRUE;
}
if (state == CHECK_DISABLED_ACTION)
{
if (g_test_verbose ())
g_print ("Verifying lack of About invocation...\n");
g_assert (!action_invoked);
state = END;
return TRUE;
}
if (state == END)
{
if (g_test_verbose ())
g_print ("Test complete\n");
g_application_quit_with_data (application, create_timestamp_data ());
return FALSE;
}
g_assert_not_reached ();
}
static void
test_basic (void)
{
GApplication *app;
const gchar *appid;
gboolean quit;
gboolean remote;
gboolean reg;
gchar **actions;
app = g_application_new ("org.gtk.TestApplication", 0, NULL);
g_assert (g_application_get_instance () == app);
g_assert_cmpstr (g_application_get_id (app), ==, "org.gtk.TestApplication");
g_object_get (app,
"application-id", &appid,
"default-quit", &quit,
"is-remote", &remote,
"register", &reg,
NULL);
g_assert_cmpstr (appid, ==, "org.gtk.TestApplication");
g_assert (quit);
g_assert (!remote);
g_assert (reg);
g_application_add_action (app, "About", "Print an about message");
g_assert (g_application_get_action_enabled (app, "About"));
g_assert_cmpstr (g_application_get_action_description (app, "About"), ==, "Print an about message");
actions = g_application_list_actions (app);
g_assert_cmpint (g_strv_length (actions), ==, 1);
g_assert_cmpstr (actions[0], ==, "About");
g_strfreev (actions);
g_application_add_action (app, "Action2", "Another action");
actions = g_application_list_actions (app);
g_assert_cmpint (g_strv_length (actions), ==, 2);
g_strfreev (actions);
g_application_remove_action (app, "Action2");
actions = g_application_list_actions (app);
g_assert_cmpint (g_strv_length (actions), ==, 1);
g_strfreev (actions);
g_signal_connect (app, "action-with-data::About", G_CALLBACK (on_app_action), NULL);
state = INVOKE_ACTION;
g_timeout_add (100, check_invoke_action, app);
g_application_run (app);
g_assert (state == END);
g_object_unref (app);
}
int
main (int argc, char *argv[])
{
gint ret;
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
session_bus_up ();
g_test_add_func ("/application/basic", test_basic);
ret = g_test_run ();
session_bus_down ();
return ret;
}

View File

@ -1,107 +0,0 @@
#include <gio/gio.h>
#include <gstdio.h>
#include <string.h>
#ifdef G_OS_UNIX
#include <stdlib.h>
#include <fcntl.h>
#endif
static gboolean action3_added = FALSE;
static void
on_app_action (GApplication *application,
const gchar *action_name,
GVariant *platform_data)
{
if (strcmp (action_name, "action1") == 0)
exit (1);
else if (strcmp (action_name, "action2") == 0)
{
if (action3_added)
g_application_remove_action (application, "action3");
else
g_application_add_action (application, "action3", "An extra action");
action3_added = !action3_added;
}
}
static void
on_app_activated (GApplication *application,
GVariant *args,
GVariant *platform_data)
{
GVariantIter iter;
const char *key;
GVariant *value;
char *cwd;
cwd = g_get_current_dir ();
g_variant_iter_init (&iter, platform_data);
while (g_variant_iter_next (&iter, "{&sv}", &key, &value))
{
const char *activate_cwd;
if (strcmp (key, "cwd") != 0)
continue;
activate_cwd = g_variant_get_bytestring (value);
g_assert_cmpstr (cwd, ==, activate_cwd);
g_variant_unref (value);
}
g_free (cwd);
}
static gboolean
on_monitor_fd_io (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
exit (0);
return FALSE;
}
int
main (int argc, char *argv[])
{
GApplication *app;
#ifdef G_OS_UNIX
{
const char *slave_fd_env = g_getenv ("_G_TEST_SLAVE_FD");
if (slave_fd_env)
{
int slave_fd = atoi (slave_fd_env);
fcntl (slave_fd, F_SETFD, FD_CLOEXEC);
g_io_add_watch (g_io_channel_unix_new (slave_fd), G_IO_HUP | G_IO_ERR,
on_monitor_fd_io, NULL);
}
}
#endif
app = g_application_unregistered_try_new ("org.gtk.test.app",
argc, argv, NULL);
if (!(argc > 1 && strcmp (argv[1], "--non-unique") == 0))
{
if (!g_application_register (app))
exit (0);
}
if (g_application_is_remote (app))
{
g_application_invoke_action (app, "action1", 0);
}
else
{
g_application_add_action (app, "action1", "Action1");
g_application_add_action (app, "action2", "Action2");
g_signal_connect (app, "action-with-data",
G_CALLBACK (on_app_action), NULL);
g_signal_connect (app, "prepare-activation",
G_CALLBACK (on_app_activated), NULL);
g_application_run (app);
}
return 0;
}

View File

@ -1,604 +0,0 @@
#include <gio/gio.h>
#ifdef G_OS_UNIX
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#endif
#include "gdbus-sessionbus.h"
static gint appeared;
static gint disappeared;
static gint changed;
static void
name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
GMainLoop *loop = user_data;
appeared++;
if (loop)
g_main_loop_quit (loop);
}
static void
name_disappeared (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GMainLoop *loop = user_data;
disappeared++;
if (loop)
g_main_loop_quit (loop);
}
#ifdef G_OS_UNIX
void
child_setup_pipe (gpointer user_data)
{
int *fds = user_data;
close (fds[0]);
dup2 (fds[1], 3);
g_setenv ("_G_TEST_SLAVE_FD", "3", TRUE);
close (fds[1]);
}
#endif
static gboolean
spawn_async_with_monitor_pipe (const gchar *argv[], GPid *pid, int *fd)
{
#ifdef G_OS_UNIX
int fds[2];
gboolean result;
pipe (fds);
result = g_spawn_async (NULL, (char**)argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, child_setup_pipe, &fds, pid, NULL);
close (fds[1]);
*fd = fds[0];
return result;
#else
*fd = -1;
return g_spawn_async (NULL, argv, 0, NULL, NULL, pid, NULL);
#endif
}
static gboolean
start_application (GPid *pid, int *fd)
{
const gchar *argv[] = { "./testapp", NULL };
g_assert (spawn_async_with_monitor_pipe (argv, pid, fd));
return FALSE;
}
typedef struct {
GMainContext *context;
GSource *child_watch;
GSource *timeout;
GPid pid;
int fd;
gboolean child_exited;
GMainLoop *loop;
} AwaitChildTerminationData;
static void
on_child_termination_exited (GPid pid,
gint status,
gpointer user_data)
{
AwaitChildTerminationData *data = user_data;
data->child_exited = TRUE;
g_spawn_close_pid (data->pid);
g_main_loop_quit (data->loop);
}
static gboolean
on_child_termination_timeout (gpointer user_data)
{
AwaitChildTerminationData *data = user_data;
g_main_loop_quit (data->loop);
return FALSE;
}
static void
await_child_termination_init (AwaitChildTerminationData *data,
GPid pid,
int fd)
{
data->context = g_main_context_get_thread_default ();
data->child_exited = FALSE;
data->pid = pid;
data->fd = fd;
}
static void
await_child_termination_terminate (AwaitChildTerminationData *data)
{
#ifdef G_OS_UNIX
kill (data->pid, SIGTERM);
close (data->fd);
#endif
}
static gboolean
await_child_termination_run (AwaitChildTerminationData *data)
{
GSource *timeout_source;
GSource *child_watch_source;
data->loop = g_main_loop_new (data->context, FALSE);
child_watch_source = g_child_watch_source_new (data->pid);
g_source_set_callback (child_watch_source, (GSourceFunc) on_child_termination_exited, data, NULL);
g_source_attach (child_watch_source, data->context);
timeout_source = g_timeout_source_new_seconds (5);
g_source_set_callback (timeout_source, on_child_termination_timeout, data, NULL);
g_source_attach (timeout_source, data->context);
g_main_loop_run (data->loop);
g_source_destroy (child_watch_source);
g_source_unref (child_watch_source);
g_source_destroy (timeout_source);
g_source_unref (timeout_source);
g_main_loop_unref (data->loop);
return data->child_exited;
}
static void
terminate_child_sync (GPid pid, int fd)
{
AwaitChildTerminationData data;
await_child_termination_init (&data, pid, fd);
await_child_termination_terminate (&data);
await_child_termination_run (&data);
}
typedef void (*RunWithApplicationFunc) (void);
typedef struct {
GMainLoop *loop;
RunWithApplicationFunc func;
guint timeout_id;
} RunWithAppNameAppearedData;
static void
on_run_with_application_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
RunWithAppNameAppearedData *data = user_data;
data->func ();
g_main_loop_quit (data->loop);
}
static gboolean
on_run_with_application_timeout (gpointer user_data)
{
RunWithAppNameAppearedData *data = user_data;
data->timeout_id = 0;
g_error ("Timed out starting testapp");
return FALSE;
}
static void
run_with_application (RunWithApplicationFunc test_func)
{
GMainLoop *loop;
RunWithAppNameAppearedData data;
gint watch;
GPid main_pid;
gint main_fd;
loop = g_main_loop_new (NULL, FALSE);
data.timeout_id = 0;
data.func = test_func;
data.loop = loop;
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
on_run_with_application_name_appeared,
NULL,
&data,
NULL);
data.timeout_id = g_timeout_add_seconds (5, on_run_with_application_timeout, &data);
start_application (&main_pid, &main_fd);
g_main_loop_run (loop);
if (data.timeout_id)
{
g_source_remove (data.timeout_id);
data.timeout_id = 0;
}
g_main_loop_unref (loop);
g_bus_unwatch_name (watch);
terminate_child_sync (main_pid, main_fd);
}
/* This test starts an application, checks that its name appears
* on the bus, then starts it again and checks that the second
* instance exits right away.
*/
static void
test_unique_on_app_appeared (void)
{
GPid sub_pid;
int sub_fd;
int watch;
AwaitChildTerminationData data;
appeared = 0;
disappeared = 0;
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
name_appeared,
name_disappeared,
NULL,
NULL);
start_application (&sub_pid, &sub_fd);
await_child_termination_init (&data, sub_pid, sub_fd);
await_child_termination_run (&data);
g_bus_unwatch_name (watch);
g_assert_cmpint (appeared, ==, 1);
g_assert_cmpint (disappeared, ==, 0);
}
static void
test_unique (void)
{
run_with_application (test_unique_on_app_appeared);
}
static void
on_name_disappeared_quit (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_main_loop_quit (loop);
}
static GVariant *
create_empty_vardict ()
{
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
return g_variant_builder_end (&builder);
}
static gboolean
call_quit (gpointer data)
{
GDBusConnection *connection;
GError *error = NULL;
GVariant *res;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
res = g_dbus_connection_call_sync (connection,
"org.gtk.test.app",
"/org/gtk/test/app",
"org.gtk.Application",
"Quit",
g_variant_new ("(@a{sv})", create_empty_vardict ()),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (error)
{
g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY);
g_error_free (error);
}
if (res)
g_variant_unref (res);
return FALSE;
}
/* This test starts an application, checks that its name appears on
* the bus, then calls Quit, and verifies that the name disappears and
* the application exits.
*/
static void
test_quit_on_app_appeared (void)
{
GMainLoop *loop;
int quit_disappeared_watch;
loop = g_main_loop_new (NULL, FALSE);
quit_disappeared_watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
NULL,
on_name_disappeared_quit,
loop,
NULL);
call_quit (NULL);
g_main_loop_run (loop);
g_bus_unwatch_name (quit_disappeared_watch);
g_main_loop_unref (loop);
}
static void
test_quit (void)
{
run_with_application (test_quit_on_app_appeared);
}
static gboolean
_g_strv_has_string (const gchar* const * haystack,
const gchar *needle)
{
guint n;
for (n = 0; haystack != NULL && haystack[n] != NULL; n++)
{
if (g_strcmp0 (haystack[n], needle) == 0)
return TRUE;
}
return FALSE;
}
static gchar **
list_actions (void)
{
GDBusConnection *connection;
GVariant *res;
gchar **strv;
gchar *str;
GVariantIter *iter;
gint i;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
res = g_dbus_connection_call_sync (connection,
"org.gtk.test.app",
"/org/gtk/test/app",
"org.gtk.Application",
"ListActions",
NULL,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL);
strv = g_new0 (gchar *, 32);
g_variant_get (res, "(a{s(sb)})", &iter);
i = 0;
while (g_variant_iter_loop (iter, "{s(sb)}", &str, NULL, NULL))
{
strv[i] = g_strdup (str);
i++;
g_assert (i < 32);
}
g_variant_iter_free (iter);
strv[i] = NULL;
g_variant_unref (res);
g_object_unref (connection);
return strv;
}
static void
test_list_actions_on_app_appeared (void)
{
gchar **actions;
actions = list_actions ();
g_assert (g_strv_length (actions) == 2);
g_assert (_g_strv_has_string ((const char *const *)actions, "action1"));
g_assert (_g_strv_has_string ((const char *const *)actions, "action2"));
g_strfreev (actions);
}
/* This test start an application, waits for its name to appear on
* the bus, then calls ListActions, and verifies that it gets the expected
* actions back.
*/
static void
test_list_actions (void)
{
run_with_application (test_list_actions_on_app_appeared);
}
static gboolean
invoke_action (gpointer user_data)
{
const gchar *action = user_data;
GDBusConnection *connection;
GVariant *res;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
res = g_dbus_connection_call_sync (connection,
"org.gtk.test.app",
"/org/gtk/test/app",
"org.gtk.Application",
"InvokeAction",
g_variant_new ("(s@a{sv})",
action,
create_empty_vardict ()),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL);
if (res)
g_variant_unref (res);
g_object_unref (connection);
return FALSE;
}
/* This test starts an application, waits for it to appear,
* then invokes 'action1' and checks that it causes the application
* to exit with an exit code of 1.
*/
static void
test_invoke (void)
{
GMainLoop *loop;
int quit_disappeared_watch;
loop = g_main_loop_new (NULL, FALSE);
quit_disappeared_watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gtk.test.app",
0,
NULL,
on_name_disappeared_quit,
loop,
NULL);
g_timeout_add (0, invoke_action, "action1");
g_main_loop_run (loop);
g_bus_unwatch_name (quit_disappeared_watch);
}
static void
test_remote_on_application_appeared (void)
{
GPid sub_pid;
int sub_fd;
AwaitChildTerminationData data;
gchar *argv[] = { "./testapp", "--non-unique", NULL };
spawn_async_with_monitor_pipe ((const char **) argv, &sub_pid, &sub_fd);
await_child_termination_init (&data, sub_pid, sub_fd);
await_child_termination_run (&data);
}
static void
test_remote (void)
{
run_with_application (test_remote_on_application_appeared);
}
static void
actions_changed (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GMainLoop *loop = user_data;
g_assert_cmpstr (interface_name, ==, "org.gtk.Application");
g_assert_cmpstr (signal_name, ==, "ActionsChanged");
changed++;
g_main_loop_quit (loop);
}
static void
test_change_action_on_application_appeared (void)
{
GMainLoop *loop;
guint id;
GDBusConnection *connection;
loop = g_main_loop_new (NULL, FALSE);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
id = g_dbus_connection_signal_subscribe (connection,
NULL,
"org.gtk.Application",
"ActionsChanged",
"/org/gtk/test/app",
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
actions_changed,
loop,
NULL);
g_timeout_add (0, invoke_action, "action2");
g_main_loop_run (loop);
g_assert_cmpint (changed, >, 0);
g_dbus_connection_signal_unsubscribe (connection, id);
g_object_unref (connection);
g_main_loop_unref (loop);
}
static void
test_change_action (void)
{
run_with_application (test_change_action_on_application_appeared);
}
int
main (int argc, char *argv[])
{
gint ret;
g_type_init ();
g_test_init (&argc, &argv, NULL);
g_unsetenv ("DISPLAY");
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE);
session_bus_up ();
g_test_add_func ("/application/unique", test_unique);
g_test_add_func ("/application/quit", test_quit);
g_test_add_func ("/application/list-actions", test_list_actions);
g_test_add_func ("/application/invoke", test_invoke);
g_test_add_func ("/application/remote", test_remote);
g_test_add_func ("/application/change-action", test_change_action);
ret = g_test_run ();
session_bus_down ();
return ret;
}