mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
Merge the wip/gapplication branch
This adds a GApplication object to GIO, which is the core of an application support class, supporting - uniqueness - exporting actions (simple scripting) - standard actions (quit, activate) The implementation for Linux uses D-Bus, takes a name on the session bus, and exports a org.gtk.Application interface. Implementations for Win32 and OS X are still missing.
This commit is contained in:
parent
af78f6d418
commit
6427e93757
@ -160,6 +160,10 @@
|
||||
<xi:include href="xml/gpermission.xml"/>
|
||||
<xi:include href="xml/gsimplepermission.xml"/>
|
||||
</chapter>
|
||||
<chapter id="application">
|
||||
<title>Application support</title>
|
||||
<xi:include href="xml/gapplication.xml"/>
|
||||
</chapter>
|
||||
<chapter id="extending">
|
||||
<title>Extending GIO</title>
|
||||
<xi:include href="xml/gvfs.xml"/>
|
||||
|
@ -2619,3 +2619,35 @@ g_simple_permission_new
|
||||
<SUBSECTION Standard>
|
||||
g_simple_permission_get_type
|
||||
</SECTION>
|
||||
|
||||
<FILE>gapplication</FILE>
|
||||
GApplication
|
||||
GApplicationClass
|
||||
|
||||
g_application_new
|
||||
g_application_new_and_register
|
||||
g_application_register_with_data
|
||||
g_application_format_activation_data
|
||||
g_application_get_instance
|
||||
g_application_get_id
|
||||
g_application_add_action
|
||||
g_application_remove_action
|
||||
g_application_list_actions
|
||||
g_application_set_action_enabled
|
||||
g_application_get_action_enabled
|
||||
g_application_get_action_description
|
||||
g_application_invoke_action
|
||||
g_application_run
|
||||
g_application_quit
|
||||
g_application_is_remote
|
||||
<SUBSECTION Standard>
|
||||
G_TYPE_APPLICATION
|
||||
G_APPLICATION
|
||||
G_APPLICATION_CLASS
|
||||
G_IS_APPLICATION
|
||||
G_IS_APPLICATION_CLASS
|
||||
G_APPLICATION_GET_CLASS
|
||||
<SUBSECTION Private>
|
||||
GApplicationPrivate
|
||||
g_application_get_type
|
||||
</SECTION>
|
||||
|
@ -1,6 +1,7 @@
|
||||
g_app_info_create_flags_get_type
|
||||
g_app_info_get_type
|
||||
g_app_launch_context_get_type
|
||||
g_application_get_type
|
||||
g_ask_password_flags_get_type
|
||||
g_async_initable_get_type
|
||||
g_async_result_get_type
|
||||
|
@ -268,6 +268,8 @@ SUBDIRS += tests
|
||||
|
||||
libgio_2_0_la_SOURCES = \
|
||||
gappinfo.c \
|
||||
gapplication.c \
|
||||
gapplication.h \
|
||||
gasynchelper.c \
|
||||
gasynchelper.h \
|
||||
gasyncinitable.c \
|
||||
@ -374,6 +376,8 @@ libgio_2_0_la_SOURCES = \
|
||||
$(marshal_sources) \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST += gnullapplication.c gdbusapplication.c
|
||||
|
||||
$(libgio_2_0_la_OBJECTS): $(marshal_sources)
|
||||
|
||||
libgio_2_0_la_LIBADD = \
|
||||
@ -427,6 +431,7 @@ gio-win32-res.o: gio.rc
|
||||
|
||||
gio_headers = \
|
||||
gappinfo.h \
|
||||
gapplication.h \
|
||||
gasyncinitable.h \
|
||||
gasyncresult.h \
|
||||
gbufferedinputstream.h \
|
||||
|
@ -33,7 +33,7 @@
|
||||
* @short_description: Application information and launch contexts
|
||||
* @include: gio/gio.h
|
||||
*
|
||||
* #GAppInfo and #GAppLaunchContext are used for describing and launching
|
||||
* #GAppInfo and #GAppLaunchContext are used for describing and launching
|
||||
* applications installed on the system.
|
||||
*
|
||||
* As of GLib 2.20, URIs will always be converted to POSIX paths
|
||||
@ -117,9 +117,9 @@ g_app_info_dup (GAppInfo *appinfo)
|
||||
|
||||
/**
|
||||
* g_app_info_equal:
|
||||
* @appinfo1: the first #GAppInfo.
|
||||
* @appinfo1: the first #GAppInfo.
|
||||
* @appinfo2: the second #GAppInfo.
|
||||
*
|
||||
*
|
||||
* Checks if two #GAppInfo<!-- -->s are equal.
|
||||
*
|
||||
* Returns: %TRUE if @appinfo1 is equal to @appinfo2. %FALSE otherwise.
|
||||
@ -461,7 +461,7 @@ g_app_info_get_icon (GAppInfo *appinfo)
|
||||
* @launch_context: a #GAppLaunchContext or %NULL
|
||||
* @error: a #GError
|
||||
*
|
||||
* Launches the application. Passes @files to the launched application
|
||||
* Launches the application. Passes @files to the launched application
|
||||
* as arguments, using the optional @launch_context to get information
|
||||
* about the details of the launcher (like what screen it is on).
|
||||
* On error, @error will be set accordingly.
|
||||
@ -476,8 +476,17 @@ g_app_info_get_icon (GAppInfo *appinfo)
|
||||
* unsupported uris with strange formats like mailto:), so if you have
|
||||
* a textual uri you want to pass in as argument, consider using
|
||||
* g_app_info_launch_uris() instead.
|
||||
*
|
||||
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||
*
|
||||
* On UNIX, this function sets the <envvar>GIO_LAUNCHED_DESKTOP_FILE</envvar>
|
||||
* environment variable with the path of the launched desktop file and
|
||||
* <envvar>GIO_LAUNCHED_DESKTOP_FILE_PID</envvar> to the process
|
||||
* id of the launched process. This can be used to ignore
|
||||
* <envvar>GIO_LAUNCHED_DESKTOP_FILE</envvar>, should it be inherited
|
||||
* by further processes. The <envvar>DISPLAY</envvar> and
|
||||
* <envvar>DESKTOP_STARTUP_ID</envvar> environment variables are also
|
||||
* set, based on information provided in @launch_context.
|
||||
*
|
||||
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||
**/
|
||||
gboolean
|
||||
g_app_info_launch (GAppInfo *appinfo,
|
||||
@ -540,11 +549,11 @@ g_app_info_supports_files (GAppInfo *appinfo)
|
||||
/**
|
||||
* g_app_info_launch_uris:
|
||||
* @appinfo: a #GAppInfo
|
||||
* @uris: a #GList containing URIs to launch.
|
||||
* @uris: a #GList containing URIs to launch.
|
||||
* @launch_context: a #GAppLaunchContext or %NULL
|
||||
* @error: a #GError
|
||||
*
|
||||
* Launches the application. Passes @uris to the launched application
|
||||
* Launches the application. Passes @uris to the launched application
|
||||
* as arguments, using the optional @launch_context to get information
|
||||
* about the details of the launcher (like what screen it is on).
|
||||
* On error, @error will be set accordingly.
|
||||
@ -555,7 +564,7 @@ g_app_info_supports_files (GAppInfo *appinfo)
|
||||
* can fail to start if it runs into problems during startup. There is
|
||||
* no way to detect this.
|
||||
*
|
||||
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||
* Returns: %TRUE on successful launch, %FALSE otherwise.
|
||||
**/
|
||||
gboolean
|
||||
g_app_info_launch_uris (GAppInfo *appinfo,
|
||||
@ -600,7 +609,7 @@ g_app_info_should_show (GAppInfo *appinfo)
|
||||
* @launch_context: an optional #GAppLaunchContext.
|
||||
* @error: a #GError.
|
||||
*
|
||||
* Utility function that launches the default application
|
||||
* Utility function that launches the default application
|
||||
* registered to handle the specified uri. Synchronous I/O
|
||||
* is done on the uri to detect the type of the file if
|
||||
* required.
|
||||
@ -726,10 +735,10 @@ g_app_launch_context_init (GAppLaunchContext *launch_context)
|
||||
* @info: a #GAppInfo
|
||||
* @files: a #GList of #GFile objects
|
||||
*
|
||||
* Gets the display string for the display. This is used to ensure new
|
||||
* applications are started on the same display as the launching
|
||||
* application.
|
||||
*
|
||||
* Gets the display string for the @context. This is used to ensure new
|
||||
* applications are started on the same display as the launching
|
||||
* application, by setting the <envvar>DISPLAY</envvar> environment variable.
|
||||
*
|
||||
* Returns: a display string for the display.
|
||||
**/
|
||||
char *
|
||||
@ -757,13 +766,14 @@ g_app_launch_context_get_display (GAppLaunchContext *context,
|
||||
* @files: a #GList of of #GFile objects
|
||||
*
|
||||
* Initiates startup notification for the application and returns the
|
||||
* DESKTOP_STARTUP_ID for the launched operation, if supported.
|
||||
* <envvar>DESKTOP_STARTUP_ID</envvar> for the launched operation,
|
||||
* if supported.
|
||||
*
|
||||
* Startup notification IDs are defined in the <ulink
|
||||
* url="http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt">
|
||||
* FreeDesktop.Org Startup Notifications standard</ulink>.
|
||||
*
|
||||
* Returns: a startup notification ID for the application, or %NULL if
|
||||
* Returns: a startup notification ID for the application, or %NULL if
|
||||
* not supported.
|
||||
**/
|
||||
char *
|
||||
|
1058
gio/gapplication.c
Normal file
1058
gio/gapplication.c
Normal file
File diff suppressed because it is too large
Load Diff
151
gio/gapplication.h
Normal file
151
gio/gapplication.h
Normal file
@ -0,0 +1,151 @@
|
||||
/* GIO - GLib Input, Output and Streaming Library
|
||||
*
|
||||
* Copyright © 2010 Red Hat, Inc
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* Authors: Colin Walters <walters@verbum.org>
|
||||
* Emmanuele Bassi <ebassi@linux.intel.com>
|
||||
*/
|
||||
|
||||
#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
|
||||
#error "Only <gio/gio.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#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))
|
||||
|
||||
typedef struct _GApplication GApplication;
|
||||
typedef struct _GApplicationPrivate GApplicationPrivate;
|
||||
typedef struct _GApplicationClass GApplicationClass;
|
||||
|
||||
/**
|
||||
* GApplication:
|
||||
*
|
||||
* The <structname>GApplication</structname> structure contains private
|
||||
* data and should only be accessed using the provided API
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GApplication
|
||||
{
|
||||
/*< private >*/
|
||||
GObject parent_instance;
|
||||
|
||||
GApplicationPrivate *priv;
|
||||
};
|
||||
|
||||
/**
|
||||
* GApplicationClass:
|
||||
* @action: class handler for the #GApplication::action signal
|
||||
* @quit: class handler for the #GApplication::quit signal
|
||||
* @prepare_activation: class handler for the #GApplication::prepare-activation signal
|
||||
* @run: virtual function, called by g_application_run()
|
||||
* @format_activation_data: virtual function, called by g_application_format_activation_data()
|
||||
*
|
||||
* The <structname>GApplicationClass</structname> structure contains
|
||||
* private data only
|
||||
*
|
||||
* Since: 2.26
|
||||
*/
|
||||
struct _GApplicationClass
|
||||
{
|
||||
/*< private >*/
|
||||
GObjectClass parent_class;
|
||||
|
||||
/*< public >*/
|
||||
/* signals */
|
||||
void (* action) (GApplication *application,
|
||||
const gchar *action_name,
|
||||
guint timestamp);
|
||||
gboolean (* quit) (GApplication *application,
|
||||
guint timestamp);
|
||||
void (* prepare_activation) (GApplication *application,
|
||||
GVariant *arguments,
|
||||
GVariant *platform_data);
|
||||
|
||||
/* vfuncs */
|
||||
void (* run) (GApplication *application);
|
||||
void (*format_activation_data) (GApplication *application,
|
||||
GVariantBuilder *builder);
|
||||
|
||||
/*< 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);
|
||||
};
|
||||
GType g_application_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GApplication * g_application_new (const char *appid);
|
||||
|
||||
void g_application_register_with_data (GApplication *application,
|
||||
int argc,
|
||||
char **argv,
|
||||
GVariant *platform_data);
|
||||
|
||||
GApplication * g_application_new_and_register (const char *appid,
|
||||
int argc,
|
||||
char **argv);
|
||||
|
||||
GApplication * g_application_get_instance (void);
|
||||
G_CONST_RETURN gchar * g_application_get_id (GApplication *application);
|
||||
|
||||
void g_application_add_action (GApplication *application,
|
||||
const char *name,
|
||||
const char *description);
|
||||
void g_application_remove_action (GApplication *application,
|
||||
const char *name);
|
||||
gchar ** g_application_list_actions (GApplication *application);
|
||||
void g_application_set_action_enabled (GApplication *application,
|
||||
const char *name,
|
||||
gboolean enabled);
|
||||
gboolean g_application_get_action_enabled (GApplication *application,
|
||||
const char *name);
|
||||
G_CONST_RETURN gchar * g_application_get_action_description (GApplication *application,
|
||||
const char *name);
|
||||
void g_application_invoke_action (GApplication *application,
|
||||
const char *name,
|
||||
guint timestamp);
|
||||
|
||||
void g_application_run (GApplication *application);
|
||||
gboolean g_application_quit (GApplication *app,
|
||||
guint timestamp);
|
||||
|
||||
gboolean g_application_is_remote (GApplication *application);
|
||||
|
||||
void g_application_format_activation_data (GApplication *app,
|
||||
GVariantBuilder *builder);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_APPLICATION_H__ */
|
426
gio/gdbusapplication.c
Normal file
426
gio/gdbusapplication.c
Normal file
@ -0,0 +1,426 @@
|
||||
/* 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)
|
||||
{
|
||||
guint32 timestamp;
|
||||
g_variant_get (parameters, "(u)", ×tamp);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
|
||||
g_application_quit (app, timestamp);
|
||||
}
|
||||
else if (strcmp (method_name, "ListActions") == 0)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
GApplicationAction *value;
|
||||
GVariantBuilder builder;
|
||||
GVariant *return_args;
|
||||
GVariant *result;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||
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);
|
||||
|
||||
result = g_variant_builder_end (&builder);
|
||||
return_args = g_variant_new_tuple (&result, 1);
|
||||
g_dbus_method_invocation_return_value (invocation, return_args);
|
||||
g_variant_unref (return_args);
|
||||
g_variant_unref (result);
|
||||
}
|
||||
else if (strcmp (method_name, "InvokeAction") == 0)
|
||||
{
|
||||
const char *action_name;
|
||||
guint32 timestamp;
|
||||
GApplicationAction *action;
|
||||
|
||||
g_variant_get (parameters, "(su)", &action_name, ×tamp);
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
g_signal_emit (app, application_signals[ACTION], g_quark_from_string (action_name), action_name, (guint)timestamp);
|
||||
|
||||
g_dbus_method_invocation_return_value (invocation, NULL);
|
||||
}
|
||||
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,
|
||||
"timestamp",
|
||||
"u",
|
||||
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,
|
||||
"timestamp",
|
||||
"u",
|
||||
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 char *
|
||||
application_path_from_appid (const char *appid)
|
||||
{
|
||||
char *appid_path, *iter;
|
||||
|
||||
|
||||
appid_path = g_strconcat ("/", appid, NULL);
|
||||
for (iter = appid_path; *iter; iter++)
|
||||
{
|
||||
if (*iter == '.')
|
||||
*iter = '/';
|
||||
}
|
||||
|
||||
return appid_path;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_bus (GApplication *app)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (app->priv->session_bus == NULL)
|
||||
app->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
|
||||
if (app->priv->session_bus == NULL)
|
||||
{
|
||||
g_error ("%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
|
||||
if (app->priv->dbus_path == NULL)
|
||||
app->priv->dbus_path = application_path_from_appid (app->priv->appid);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_init (GApplication *app)
|
||||
{
|
||||
GError *error = NULL;
|
||||
guint registration_id;
|
||||
|
||||
ensure_bus (app);
|
||||
|
||||
registration_id = g_dbus_connection_register_object (app->priv->session_bus,
|
||||
app->priv->dbus_path,
|
||||
&application_dbus_interface_info,
|
||||
&application_dbus_vtable,
|
||||
app, NULL,
|
||||
&error);
|
||||
if (registration_id == 0)
|
||||
{
|
||||
g_error ("%s", error->message);
|
||||
g_error_free (error);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_g_application_platform_acquire_single_instance (GApplication *app,
|
||||
GError **error)
|
||||
{
|
||||
GVariant *request_result;
|
||||
guint32 request_status;
|
||||
|
||||
ensure_bus (app);
|
||||
if (app->priv->session_bus == NULL)
|
||||
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, NULL, error);
|
||||
|
||||
if (request_result == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (strcmp (g_variant_get_type_string (request_result), "(u)") == 0)
|
||||
g_variant_get (request_result, "(u)", &request_status);
|
||||
else
|
||||
request_status = 0;
|
||||
|
||||
g_variant_unref (request_result);
|
||||
|
||||
if (request_status != 1 && request_status != 4)
|
||||
{
|
||||
if (request_status == 3)
|
||||
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Another process has name \"%s\"", app->priv->appid);
|
||||
else
|
||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error");
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
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 char *action,
|
||||
guint timestamp)
|
||||
{
|
||||
GVariant *result;
|
||||
|
||||
ensure_bus (app);
|
||||
|
||||
result = g_dbus_connection_call_sync (app->priv->session_bus,
|
||||
app->priv->appid,
|
||||
app->priv->dbus_path,
|
||||
G_APPLICATION_IFACE,
|
||||
"InvokeAction",
|
||||
g_variant_new ("(su)",
|
||||
action,
|
||||
timestamp),
|
||||
NULL, 0, -1, NULL, NULL);
|
||||
if (result)
|
||||
g_variant_unref (result);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_remote_quit (GApplication *app,
|
||||
guint timestamp)
|
||||
{
|
||||
GVariant *result;
|
||||
|
||||
ensure_bus (app);
|
||||
|
||||
result = g_dbus_connection_call_sync (app->priv->session_bus,
|
||||
app->priv->appid,
|
||||
app->priv->dbus_path,
|
||||
G_APPLICATION_IFACE,
|
||||
"Quit",
|
||||
g_variant_new ("(u)",
|
||||
timestamp),
|
||||
NULL, 0, -1, NULL, NULL);
|
||||
if (result)
|
||||
g_variant_unref (result);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_activate (GApplication *app,
|
||||
GVariant *data)
|
||||
{
|
||||
GVariant *result;
|
||||
|
||||
ensure_bus (app);
|
||||
|
||||
result = g_dbus_connection_call_sync (app->priv->session_bus,
|
||||
app->priv->appid,
|
||||
app->priv->dbus_path,
|
||||
G_APPLICATION_IFACE,
|
||||
"Activate",
|
||||
data,
|
||||
NULL, 0, -1, NULL, NULL);
|
||||
|
||||
if (result)
|
||||
g_variant_unref (result);
|
||||
|
||||
exit (0);
|
||||
}
|
@ -434,7 +434,7 @@ gboolean g_dbus_connection_unregister_subtree (GDBusConnection
|
||||
* @connection: A #GDBusConnection.
|
||||
* @sender_name: The unique bus name of the sender of the signal.
|
||||
* @object_path: The object path that the signal was emitted on.
|
||||
* @interface_name: The name of the signal.
|
||||
* @interface_name: The name of the interface.
|
||||
* @signal_name: The name of the signal.
|
||||
* @parameters: A #GVariant tuple with parameters for the signal.
|
||||
* @user_data: User data passed when subscribing to the signal.
|
||||
|
@ -879,6 +879,7 @@ typedef struct
|
||||
{
|
||||
char *display;
|
||||
char *sn_id;
|
||||
char *desktop_file;
|
||||
} ChildSetupData;
|
||||
|
||||
static void
|
||||
@ -891,6 +892,16 @@ child_setup (gpointer user_data)
|
||||
|
||||
if (data->sn_id)
|
||||
g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
|
||||
|
||||
if (data->desktop_file)
|
||||
{
|
||||
gchar pid[20];
|
||||
|
||||
g_setenv ("GIO_LAUNCHED_DESKTOP_FILE", data->desktop_file, TRUE);
|
||||
|
||||
g_snprintf (pid, 20, "%d", getpid ());
|
||||
g_setenv ("GIO_LAUNCHED_DESKTOP_FILE_PID", pid, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -927,6 +938,7 @@ g_desktop_app_info_launch_uris (GAppInfo *appinfo,
|
||||
|
||||
data.display = NULL;
|
||||
data.sn_id = NULL;
|
||||
data.desktop_file = info->filename;
|
||||
|
||||
if (launch_context)
|
||||
{
|
||||
|
@ -9,3 +9,6 @@ BOOL:UINT
|
||||
VOID:STRING,STRING,BOXED
|
||||
VOID:BOOL,BOXED
|
||||
VOID:BOXED,BOXED
|
||||
VOID:INT
|
||||
VOID:STRING,INT
|
||||
VOID:BOXED,BOXED
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <gio/giotypes.h>
|
||||
|
||||
#include <gio/gappinfo.h>
|
||||
#include <gio/gapplication.h>
|
||||
#include <gio/gasyncinitable.h>
|
||||
#include <gio/gasyncresult.h>
|
||||
#include <gio/gbufferedinputstream.h>
|
||||
|
@ -26,6 +26,27 @@ g_vfs_get_local
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__G_APPLICATION_H__)
|
||||
#if IN_FILE(__G_APPLICATION_C__)
|
||||
g_application_get_type G_GNUC_CONST
|
||||
g_application_new
|
||||
g_application_new_and_register
|
||||
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_register_with_data
|
||||
g_application_invoke_action
|
||||
g_application_list_actions
|
||||
g_application_run
|
||||
g_application_quit
|
||||
g_application_is_remote
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if IN_HEADER(__G_APP_INFO_H__)
|
||||
#if IN_FILE(__G_APP_INFO_C__)
|
||||
g_app_info_get_type G_GNUC_CONST
|
||||
|
70
gio/gnullapplication.c
Normal file
70
gio/gnullapplication.c
Normal file
@ -0,0 +1,70 @@
|
||||
/* 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>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "gioerror.h"
|
||||
|
||||
static void
|
||||
_g_application_platform_init (GApplication *app)
|
||||
{
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_g_application_platform_acquire_single_instance (GApplication *app,
|
||||
GError **error)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_on_actions_changed (GApplication *app)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_remote_invoke_action (GApplication *app,
|
||||
const char *action,
|
||||
guint timestamp)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_remote_quit (GApplication *app,
|
||||
guint timestamp)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_default_quit (void)
|
||||
{
|
||||
exit (0);
|
||||
}
|
||||
|
||||
static void
|
||||
_g_application_platform_activate (GApplication *app,
|
||||
GVariant *data)
|
||||
{
|
||||
exit (0);
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ TEST_PROGS += \
|
||||
gdbus-error \
|
||||
gdbus-peer \
|
||||
gdbus-exit-on-close \
|
||||
application \
|
||||
testapps \
|
||||
appinfo \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
@ -73,6 +76,8 @@ SAMPLE_PROGS = \
|
||||
gdbus-example-subtree \
|
||||
gdbus-example-peer \
|
||||
gdbus-example-proxy-subclass \
|
||||
testapp \
|
||||
appinfo-test \
|
||||
$(NULL)
|
||||
|
||||
|
||||
@ -248,6 +253,21 @@ gdbus_example_proxy_subclass_LDADD = $(progs_ldadd)
|
||||
gdbus_example_export_SOURCES = gdbus-example-export.c
|
||||
gdbus_example_export_LDADD = $(progs_ldadd)
|
||||
|
||||
application_SOURCES = application.c
|
||||
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
|
||||
testapps_LDADD = $(progs_ldadd)
|
||||
|
||||
EXTRA_DIST += \
|
||||
socket-common.c \
|
||||
org.gtk.test.gschema \
|
||||
|
20
gio/tests/appinfo-test.c
Normal file
20
gio/tests/appinfo-test.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <stdlib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
const gchar *envvar;
|
||||
gint pid_from_env;
|
||||
|
||||
envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE_PID");
|
||||
g_assert (envvar != NULL);
|
||||
pid_from_env = atoi (envvar);
|
||||
g_assert_cmpint (pid_from_env, ==, getpid ());
|
||||
|
||||
envvar = g_getenv ("GIO_LAUNCHED_DESKTOP_FILE");
|
||||
g_assert_cmpstr (envvar, ==, SRCDIR "/appinfo-test.desktop");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
4
gio/tests/appinfo-test.desktop
Normal file
4
gio/tests/appinfo-test.desktop
Normal file
@ -0,0 +1,4 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=appinfo-test
|
||||
Exec=./appinfo-test
|
25
gio/tests/appinfo.c
Normal file
25
gio/tests/appinfo.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
|
||||
static void
|
||||
test_launch (void)
|
||||
{
|
||||
GAppInfo *appinfo;
|
||||
|
||||
appinfo = (GAppInfo*)g_desktop_app_info_new_from_filename (SRCDIR "/appinfo-test.desktop");
|
||||
g_assert (appinfo != NULL);
|
||||
|
||||
g_assert (g_app_info_launch (appinfo, NULL, NULL, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_type_init ();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/appinfo/launch", test_launch);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
134
gio/tests/application.c
Normal file
134
gio/tests/application.c
Normal file
@ -0,0 +1,134 @@
|
||||
#include <stdlib.h>
|
||||
#include <gio.h>
|
||||
#include <gstdio.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,
|
||||
guint action_timestamp)
|
||||
{
|
||||
if (g_test_verbose ())
|
||||
g_print ("Action '%s' invoked (timestamp: %u, expected: %u)\n",
|
||||
action_name,
|
||||
action_timestamp,
|
||||
timestamp);
|
||||
|
||||
g_assert_cmpstr (action_name, ==, "About");
|
||||
g_assert_cmpint (action_timestamp, ==, timestamp);
|
||||
|
||||
action_invoked = TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
check_invoke_action (gpointer data)
|
||||
{
|
||||
GApplication *application = data;
|
||||
|
||||
if (state == INVOKE_ACTION)
|
||||
{
|
||||
timestamp = (guint) time (NULL);
|
||||
|
||||
if (g_test_verbose ())
|
||||
g_print ("Invoking About...\n");
|
||||
|
||||
g_application_invoke_action (application, "About", timestamp);
|
||||
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", (guint) time (NULL));
|
||||
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 (application, (guint) time (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
static void
|
||||
test_basic (void)
|
||||
{
|
||||
GApplication *app;
|
||||
|
||||
app = g_application_new_and_register ("org.gtk.TestApplication", 0, NULL);
|
||||
g_application_add_action (app, "About", "Print an about message");
|
||||
|
||||
g_signal_connect (app, "action::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[])
|
||||
{
|
||||
g_type_init ();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/application/basic", test_basic);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
79
gio/tests/testapp.c
Normal file
79
gio/tests/testapp.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include <stdlib.h>
|
||||
#include <gio.h>
|
||||
#include <gstdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static gboolean action3_added = FALSE;
|
||||
|
||||
static void
|
||||
on_app_action (GApplication *application,
|
||||
const gchar *action_name,
|
||||
guint action_timestamp)
|
||||
{
|
||||
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 gboolean
|
||||
invoke_action1 (gpointer data)
|
||||
{
|
||||
GApplication *app = data;
|
||||
|
||||
g_application_invoke_action (app, "action1", 0);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_app_activated (GApplication *application,
|
||||
GVariant *args,
|
||||
GVariant *platform_data)
|
||||
{
|
||||
char *str;
|
||||
|
||||
g_print ("got args: ");
|
||||
str = g_variant_print (args, TRUE);
|
||||
g_print ("%s ", str);
|
||||
g_free (str);
|
||||
str = g_variant_print (platform_data, TRUE);
|
||||
g_print ("%s\n", str);
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GApplication *app;
|
||||
GMainLoop *loop;
|
||||
|
||||
app = g_application_new ("org.gtk.test.app");
|
||||
if (!(argc > 1 && strcmp (argv[1], "--non-unique") == 0))
|
||||
g_application_register_with_data (app, argc, argv, NULL);
|
||||
|
||||
if (g_application_is_remote (app))
|
||||
{
|
||||
g_timeout_add (1000, invoke_action1, app);
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_main_loop_run (loop);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_application_add_action (app, "action1", "Action1");
|
||||
g_application_add_action (app, "action2", "Action2");
|
||||
g_signal_connect (app, "action",
|
||||
G_CALLBACK (on_app_action), NULL);
|
||||
g_signal_connect (app, "prepare-activation",
|
||||
G_CALLBACK (on_app_activated), NULL);
|
||||
g_application_run (app);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
533
gio/tests/testapps.c
Normal file
533
gio/tests/testapps.c
Normal file
@ -0,0 +1,533 @@
|
||||
#include <gio/gio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
|
||||
static gint appeared;
|
||||
static gint disappeared;
|
||||
static gint changed;
|
||||
static gboolean died;
|
||||
static gboolean timed_out;
|
||||
GPid pid;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
start_application (gpointer data)
|
||||
{
|
||||
gchar *argv[] = { "./testapp", NULL };
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, NULL));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
run_application_sync (gpointer data)
|
||||
{
|
||||
GMainLoop *loop = data;
|
||||
|
||||
g_assert (g_spawn_command_line_sync ("./testapp", NULL, NULL, NULL, NULL));
|
||||
|
||||
if (loop)
|
||||
g_main_loop_quit (loop);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
timeout (gpointer data)
|
||||
{
|
||||
GMainLoop *loop = data;
|
||||
|
||||
timed_out = TRUE;
|
||||
|
||||
g_main_loop_quit (loop);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* 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 (void)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
gint watch;
|
||||
guint id1, id2, id3;
|
||||
|
||||
appeared = 0;
|
||||
timed_out = FALSE;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
id1 = g_timeout_add (5000, timeout, loop);
|
||||
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
NULL,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
id2 = g_timeout_add (0, start_application, loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
|
||||
id3 = g_timeout_add (0, run_application_sync, loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
g_assert_cmpint (timed_out, ==, FALSE);
|
||||
|
||||
g_bus_unwatch_name (watch);
|
||||
|
||||
kill (pid, SIGTERM);
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
g_source_remove (id1);
|
||||
g_source_remove (id2);
|
||||
g_source_remove (id3);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
quit_app (gpointer 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",
|
||||
"Quit",
|
||||
g_variant_new ("(u)", 0),
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL);
|
||||
if (res)
|
||||
g_variant_unref (res);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
child_is_dead (GPid pid,
|
||||
gint status,
|
||||
gpointer data)
|
||||
{
|
||||
GMainLoop *loop = data;
|
||||
|
||||
died++;
|
||||
|
||||
g_assert (WIFEXITED (status) && WEXITSTATUS(status) == 0);
|
||||
|
||||
if (loop)
|
||||
g_main_loop_quit (loop);
|
||||
}
|
||||
|
||||
/* This test start 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 (void)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
gint watch;
|
||||
guint id1, id2, id3;
|
||||
gchar *argv[] = { "./testapp", NULL };
|
||||
|
||||
appeared = 0;
|
||||
disappeared = 0;
|
||||
died = FALSE;
|
||||
timed_out = FALSE;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
name_disappeared,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL));
|
||||
|
||||
id1 = g_child_watch_add (pid, child_is_dead, loop);
|
||||
|
||||
id2 = g_timeout_add (500, quit_app, NULL);
|
||||
|
||||
id3 = g_timeout_add (5000, timeout, loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (timed_out, ==, FALSE);
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
g_assert_cmpint (disappeared, >=, 1);
|
||||
g_assert_cmpint (died, ==, TRUE);
|
||||
|
||||
g_bus_unwatch_name (watch);
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
g_source_remove (id1);
|
||||
g_source_remove (id2);
|
||||
g_source_remove (id3);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
gchar *argv[] = { "./testapp", NULL };
|
||||
gchar **actions;
|
||||
gint watch;
|
||||
|
||||
appeared = 0;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
NULL,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, 0, NULL, NULL, &pid, NULL));
|
||||
if (!appeared)
|
||||
g_main_loop_run (loop);
|
||||
g_main_loop_unref (loop);
|
||||
|
||||
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);
|
||||
|
||||
kill (pid, SIGTERM);
|
||||
|
||||
g_bus_unwatch_name (watch);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
invoke_action (gpointer data)
|
||||
{
|
||||
const gchar *action = 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 ("(su)",
|
||||
action,
|
||||
0),
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL);
|
||||
if (res)
|
||||
g_variant_unref (res);
|
||||
g_object_unref (connection);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
exit_with_code_1 (GPid pid,
|
||||
gint status,
|
||||
gpointer data)
|
||||
{
|
||||
GMainLoop *loop = data;
|
||||
|
||||
died++;
|
||||
|
||||
g_assert (WIFEXITED (status) && WEXITSTATUS(status) == 1);
|
||||
|
||||
if (loop)
|
||||
g_main_loop_quit (loop);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
gint watch;
|
||||
gchar *argv[] = { "./testapp", NULL };
|
||||
guint id1, id2, id3;
|
||||
|
||||
appeared = 0;
|
||||
disappeared = 0;
|
||||
died = FALSE;
|
||||
timed_out = FALSE;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
name_disappeared,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, NULL));
|
||||
|
||||
id1 = g_child_watch_add (pid, exit_with_code_1, loop);
|
||||
|
||||
id2 = g_timeout_add (500, invoke_action, "action1");
|
||||
|
||||
id3 = g_timeout_add (5000, timeout, loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
g_assert_cmpint (timed_out, ==, FALSE);
|
||||
g_assert_cmpint (appeared, >=, 1);
|
||||
g_assert_cmpint (disappeared, >=, 1);
|
||||
g_assert_cmpint (died, ==, TRUE);
|
||||
|
||||
g_bus_unwatch_name (watch);
|
||||
g_main_loop_unref (loop);
|
||||
g_source_remove (id1);
|
||||
g_source_remove (id2);
|
||||
g_source_remove (id3);
|
||||
|
||||
kill (pid, SIGTERM);
|
||||
}
|
||||
|
||||
static void
|
||||
test_remote (void)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
gint watch;
|
||||
GPid pid1, pid2;
|
||||
gchar *argv[] = { "./testapp", NULL, NULL };
|
||||
|
||||
appeared = 0;
|
||||
timed_out = FALSE;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_timeout_add (5000, timeout, loop);
|
||||
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
NULL,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid1, NULL));
|
||||
g_child_watch_add (pid1, exit_with_code_1, loop);
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
|
||||
argv[1] = "--non-unique";
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid2, NULL));
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
g_assert_cmpint (timed_out, ==, FALSE);
|
||||
|
||||
g_main_loop_unref (loop);
|
||||
g_bus_unwatch_name (watch);
|
||||
|
||||
kill (pid1, SIGTERM);
|
||||
kill (pid2, SIGTERM);
|
||||
}
|
||||
|
||||
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 (void)
|
||||
{
|
||||
GMainLoop *loop;
|
||||
gint watch;
|
||||
guint id;
|
||||
GPid pid1;
|
||||
gchar *argv[] = { "./testapp", NULL, NULL };
|
||||
GDBusConnection *connection;
|
||||
|
||||
appeared = 0;
|
||||
changed = 0;
|
||||
timed_out = FALSE;
|
||||
|
||||
loop = g_main_loop_new (NULL, FALSE);
|
||||
g_timeout_add (5000, timeout, loop);
|
||||
|
||||
watch = g_bus_watch_name (G_BUS_TYPE_SESSION,
|
||||
"org.gtk.test.app",
|
||||
0,
|
||||
name_appeared,
|
||||
NULL,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
g_assert (g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid1, NULL));
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (appeared, ==, 1);
|
||||
|
||||
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,
|
||||
actions_changed,
|
||||
loop,
|
||||
NULL);
|
||||
|
||||
g_timeout_add (1000, invoke_action, "action2");
|
||||
|
||||
g_main_loop_run (loop);
|
||||
|
||||
g_assert_cmpint (changed, >, 0);
|
||||
g_assert_cmpint (timed_out, ==, FALSE);
|
||||
|
||||
g_dbus_connection_signal_unsubscribe (connection, id);
|
||||
g_object_unref (connection);
|
||||
g_main_loop_unref (loop);
|
||||
g_bus_unwatch_name (watch);
|
||||
|
||||
kill (pid1, SIGTERM);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_type_init ();
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
|
||||
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);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user