Commit 6427e937 authored by Matthias Clasen's avatar Matthias Clasen

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.
parent af78f6d4
......@@ -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>
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 *
......
This diff is collapsed.
/* 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__ */
/* 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