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:
Matthias Clasen 2010-06-07 13:25:39 -04:00
parent af78f6d418
commit 6427e93757
21 changed files with 2626 additions and 17 deletions

View File

@ -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"/>

View File

@ -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>

View File

@ -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

View File

@ -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 \

View File

@ -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

File diff suppressed because it is too large Load Diff

151
gio/gapplication.h Normal file
View 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
View 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)", &timestamp);
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, &timestamp);
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);
}

View File

@ -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.

View File

@ -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)
{

View File

@ -9,3 +9,6 @@ BOOL:UINT
VOID:STRING,STRING,BOXED
VOID:BOOL,BOXED
VOID:BOXED,BOXED
VOID:INT
VOID:STRING,INT
VOID:BOXED,BOXED

View File

@ -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>

View File

@ -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
View 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);
}

View File

@ -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
View 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;
}

View File

@ -0,0 +1,4 @@
[Desktop Entry]
Type=Application
Name=appinfo-test
Exec=./appinfo-test

25
gio/tests/appinfo.c Normal file
View 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
View 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
View 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
View 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 ();
}