/* 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 * Emmanuele Bassi */ #include "config.h" #include #include #include #include "gapplication.h" #include "gio-marshal.h" #include "glibintl.h" #include "gioerror.h" #include "ginitable.h" #include "gdbusconnection.h" #include "gdbusintrospection.h" #include "gdbusmethodinvocation.h" /** * SECTION: gapplication * @title: GApplication * @short_description: Core application class * * A #GApplication is the foundation of an application, unique for a * given application identifier. The #GApplication wraps some * low-level platform-specific services and is intended to act as the * foundation for higher-level application classes such as * #GtkApplication or #MxApplication. In general, you should not use * this class outside of a higher level framework. By default, * g_application_register_with_data() will invoke g_error() if it is * run in a context where it cannot support its core features. Note * that g_error() is by default fatal. * * One of the core features that #GApplication provides is process * uniqueness, in the context of a "session". The session concept is * platform-dependent, but corresponds roughly to a graphical desktop * login. When your application is launched again, its arguments * are passed through platform communication to the already running * program. * * In addition, #GApplication provides support for 'actions', which * can be presented to the user in a platform-specific way * (e.g. Windows 7 jump lists). Note that these are just simple * actions without parameters. For more flexible scriptability, * implementing a a separate D-Bus interface is recommended, see e.g. * . * * Finally, #GApplication acts as a basic lifecycle root; see the * g_application_run() and g_application_quit_with_data() methods. * * Before using #GApplication, you must choose an "application identifier". * The expected form of an application identifier is very close to that of * of a DBus bus name. * Examples include: "com.example.MyApp" "org.example.internal-apps.Calculator" * For convenience, the restrictions on application identifiers are reproduced * here: * * Application identifiers must contain only the ASCII characters "[A-Z][a-z][0-9]_-" and must not begin with a digit. * Application identifiers must contain at least one '.' (period) character (and thus at least two elements). * Application identifiers must not begin with a '.' (period) character. * Application identifiers must not exceed 255 characters. * * * D-Bus implementation * * On UNIX systems using D-Bus, #GApplication is implemented by claiming the * application identifier as a bus name on the session bus. The implementation * exports an object at the object path that is created by replacing '.' with * '/' in the application identifier (e.g. the object path for the * application id 'org.gtk.TestApp' is '/org/gtk/TestApp'). The object * implements the org.gtk.Application interface. * * * org.gtk.Application * * * Activate * inaayarguments * ina{sv}data * * * * InvokeAction * insaction * ina{sv}data * * * a{s(sb)} * ListActions * * * * * Quit * ina{sv}data * * * Signal * * ActionsChanged * * * * * The Activate function is called on the existing * application instance when a second instance fails to take the bus name. * @arguments contains the commandline arguments given to the second instance * and @data contains platform-specific additional data. * * On all platforms, @data will have a key "cwd" of type signature * "ay" which contains the working directory of the invoked * executable; this data is defined to be in the default GLib * filesystem encoding for the platform. See g_filename_to_utf8(). * * * * The InvokeAction function can be called to * invoke one of the actions exported by the application. On X11 * platforms, the platform_data argument should have a "timestamp" * parameter of type "u" with the server time of the initiating event. * * * The ListActions function returns a dictionary * with the exported actions of the application. The keys of the dictionary * are the action names, and the values are structs containing the description * for the action and a boolean that represents if the action is enabled or not. * * * The Quit function can be called to * terminate the application. The @data parameter contains * platform-specific data. On X11 platforms, the platform_data * argument should have a "timestamp" parameter of type "u" with the * server time of the initiating event. * * * The ActionsChanged signal is emitted when the * exported actions change (i.e. an action is added, removed, enabled, * disabled, or otherwise changed). * * * #GApplication is supported since Gio 2.26. * * */ static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GApplication, g_application, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); enum { PROP_0, PROP_APPLICATION_ID, PROP_REGISTER, PROP_DEFAULT_QUIT, PROP_IS_REMOTE, PROP_ARGV, PROP_PLATFORM_DATA }; enum { QUIT_WITH_DATA, ACTION_WITH_DATA, PREPARE_ACTIVATION, LAST_SIGNAL }; static guint application_signals[LAST_SIGNAL] = { 0 }; typedef struct { gchar *name; gchar *description; guint enabled : 1; } GApplicationAction; struct _GApplicationPrivate { gchar *appid; GHashTable *actions; /* name -> GApplicationAction */ GMainLoop *mainloop; GVariant *argv; GVariant *platform_data; guint do_register : 1; guint default_quit : 1; guint is_remote : 1; guint registration_tried : 1; guint actions_changed_id; #ifdef G_OS_UNIX gchar *dbus_path; GDBusConnection *session_bus; #endif }; static GApplication *primary_application = NULL; static GHashTable *instances_for_appid = NULL; static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error); static gboolean _g_application_platform_init (GApplication *app, GCancellable *cancellable, GError **error); static gboolean _g_application_platform_register (GApplication *app, gboolean *unique, GCancellable *cancellable, GError **error); static void _g_application_platform_remote_invoke_action (GApplication *app, const gchar *action, GVariant *platform_data); static void _g_application_platform_remote_quit (GApplication *app, GVariant *platform_data); static void _g_application_platform_on_actions_changed (GApplication *app); static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = initable_init; } #ifdef G_OS_UNIX #include "gdbusapplication.c" #else #include "gnullapplication.c" #endif static gboolean _g_application_validate_id (const char *id) { gboolean allow_dot; if (strlen (id) > 255) return FALSE; if (!g_ascii_isalpha (*id)) return FALSE; id++; allow_dot = FALSE; for (; *id; id++) { if (g_ascii_isalnum (*id) || (*id == '-') || (*id == '_')) allow_dot = TRUE; else if (allow_dot && *id == '.') allow_dot = FALSE; else return FALSE; } return TRUE; } static gpointer init_appid_statics (gpointer data) { instances_for_appid = g_hash_table_new (g_str_hash, g_str_equal); return NULL; } static GApplication * application_for_appid (const char *appid) { static GOnce appid_once = G_ONCE_INIT; g_once (&appid_once, init_appid_statics, NULL); return g_hash_table_lookup (instances_for_appid, appid); } static gboolean g_application_default_quit_with_data (GApplication *application, GVariant *platform_data) { g_return_val_if_fail (application->priv->mainloop != NULL, FALSE); g_main_loop_quit (application->priv->mainloop); return TRUE; } static void g_application_default_run (GApplication *application) { if (application->priv->mainloop == NULL) application->priv->mainloop = g_main_loop_new (NULL, TRUE); g_main_loop_run (application->priv->mainloop); } static GVariant * append_cwd_to_platform_data (GVariant *platform_data) { GVariantBuilder builder; gchar *cwd; GVariant *result; cwd = g_get_current_dir (); g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (cwd) g_variant_builder_add (&builder, "{sv}", "cwd", g_variant_new_bytestring (cwd)); g_free (cwd); if (platform_data) { GVariantIter iter; GVariant *item; g_variant_iter_init (&iter, platform_data); while (g_variant_iter_next (&iter, "@{sv}", &item)) { g_variant_builder_add_value (&builder, item); g_variant_unref (item); } } result = g_variant_builder_end (&builder); return result; } static gboolean timeout_handle_actions_changed (gpointer user_data) { GApplication *application = user_data; application->priv->actions_changed_id = 0; _g_application_platform_on_actions_changed (application); return FALSE; } static inline void queue_actions_change_notification (GApplication *application) { GApplicationPrivate *priv = application->priv; if (priv->actions_changed_id == 0) priv->actions_changed_id = g_timeout_add (0, timeout_handle_actions_changed, application); } static gboolean initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GApplication *app = G_APPLICATION (initable); gboolean unique; if (!_g_application_platform_init (app, cancellable, error)) return FALSE; if (app->priv->do_register && !_g_application_platform_register (app, &unique, cancellable ,error)) return FALSE; return TRUE; } static void g_application_action_free (gpointer data) { if (G_LIKELY (data != NULL)) { GApplicationAction *action = data; g_free (action->name); g_free (action->description); g_slice_free (GApplicationAction, action); } } /** * g_application_new: * @appid: System-dependent application identifier * @argc: Number of arguments in @argv * @argv: (allow-none) (array length=argc): Argument vector, usually from the argv parameter of main() * * Create a new #GApplication. This uses a platform-specific * mechanism to ensure the current process is the unique owner of the * application (as defined by the @appid). If successful, the * #GApplication:is-remote property will be %FALSE, and it is safe to * continue creating other resources such as graphics windows. * * If the given @appid is already running in another process, the the * GApplication::activate_with_data signal will be emitted in the * remote process, with the data from @argv and other * platform-specific data available. Subsequently the * #GApplication:default-quit property will be evaluated. If it's * %TRUE, then the current process will terminate. If %FALSE, then * the application remains in the #GApplication:is-remote state, and * you can e.g. call g_application_invoke_action(). Note that proxy * instances should not call g_application_add_action(). * * This function may do synchronous I/O to obtain unique ownership * of the application id, and will block the calling thread in this * case. * * If the environment does not support the basic functionality of * #GApplication, this function will invoke g_error(), which by * default is a fatal operation. This may arise for example on * UNIX systems using D-Bus when the session bus is not available. * * As a convenience, this function is defined to call g_type_init() as * its very first action. * * Returns: (transfer full): An application instance * * Since: 2.26 */ GApplication * g_application_new (const gchar *appid, int argc, char **argv) { const gchar * const *args = (const gchar **) argv; GObject *app; GError *error = NULL; GVariant *argv_variant; g_type_init (); g_return_val_if_fail (appid != NULL, NULL); argv_variant = g_variant_new_bytestring_array (args, argc); app = g_initable_new (G_TYPE_APPLICATION, NULL, &error, "application-id", appid, "argv", argv_variant, NULL); if (!app) { g_error ("%s", error->message); g_clear_error (&error); return NULL; } return G_APPLICATION (app); } /** * g_application_try_new: * @appid: System-dependent application identifier * @argc: Number of arguments in @argv * @argv: (allow-none) (array length=argc): Argument vector, usually from the argv parameter of main() * @error: a #GError * * This function is similar to g_application_new(), but allows for * more graceful fallback if the environment doesn't support the * basic #GApplication functionality. * * Returns: (transfer full): An application instance * * Since: 2.26 */ GApplication * g_application_try_new (const gchar *appid, int argc, char **argv, GError **error) { const gchar * const *args = (const gchar **) argv; GVariant *argv_variant; g_type_init (); g_return_val_if_fail (appid != NULL, NULL); argv_variant = g_variant_new_bytestring_array (args, argc); return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, NULL, error, "application-id", appid, "argv", argv_variant, NULL)); } /** * g_application_unregistered_try_new: * @appid: System-dependent application identifier * @argc: Number of arguments in @argv * @argv: (allow-none) (array length=argc): Argument vector, usually from the argv parameter of main() * @error: a #GError * * This function is similar to g_application_try_new(), but also * sets the GApplication:register property to %FALSE. You can later * call g_application_register() to complete initialization. * * Returns: (transfer full): An application instance * * Since: 2.26 */ GApplication * g_application_unregistered_try_new (const gchar *appid, int argc, char **argv, GError **error) { const gchar * const *args = (const gchar **) argv; GVariant *argv_variant; g_type_init (); g_return_val_if_fail (appid != NULL, NULL); argv_variant = g_variant_new_bytestring_array (args, argc); return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, NULL, error, "application-id", appid, "argv", argv_variant, "register", FALSE, NULL)); } /** * g_application_register: * @application: a #GApplication * * By default, #GApplication ensures process uniqueness when * initialized, but this behavior is controlled by the * GApplication:register property. If it was given as %FALSE at * construction time, this function allows you to later attempt * to ensure uniqueness. Note that the GApplication:default-quit * property no longer applies at this point; if this function returns * %FALSE, platform activation will occur, but the current process * will not be terminated. * * It is an error to call this function more than once. It is * also an error to call this function if the GApplication:register * property was %TRUE at construction time. * * Returns: %TRUE if registration was successful */ gboolean g_application_register (GApplication *application) { gboolean unique; g_return_val_if_fail (G_IS_APPLICATION (application), FALSE); g_return_val_if_fail (application->priv->is_remote, FALSE); g_return_val_if_fail (!application->priv->registration_tried, FALSE); if (!_g_application_platform_register (application, &unique, NULL, NULL)) return FALSE; return unique; } /** * g_application_add_action: * @application: a #GApplication * @name: the action name * @description: the action description; can be a translatable * string * * Adds an action @name to the list of exported actions of @application. * * It is an error to call this function if @application is a proxy for * a remote application. * * You can invoke an action using g_application_invoke_action(). * * The newly added action is enabled by default; you can call * g_application_set_action_enabled() to disable it. * * Since: 2.26 */ void g_application_add_action (GApplication *application, const gchar *name, const gchar *description) { GApplicationPrivate *priv; GApplicationAction *action; g_return_if_fail (G_IS_APPLICATION (application)); g_return_if_fail (name != NULL && *name != '\0'); g_return_if_fail (!application->priv->is_remote); priv = application->priv; g_return_if_fail (g_hash_table_lookup (priv->actions, name) == NULL); action = g_slice_new (GApplicationAction); action->name = g_strdup (name); action->description = g_strdup (description); action->enabled = TRUE; g_hash_table_insert (priv->actions, action->name, action); queue_actions_change_notification (application); } /** * g_application_remove_action: * @application: a #GApplication * @name: the name of the action to remove * * Removes the action @name from the list of exported actions of @application. * * It is an error to call this function if @application is a proxy for * a remote application. * * Since: 2.26 */ void g_application_remove_action (GApplication *application, const gchar *name) { GApplicationPrivate *priv; g_return_if_fail (G_IS_APPLICATION (application)); g_return_if_fail (name != NULL && *name != '\0'); g_return_if_fail (!application->priv->is_remote); priv = application->priv; g_return_if_fail (g_hash_table_lookup (priv->actions, name) != NULL); g_hash_table_remove (priv->actions, name); queue_actions_change_notification (application); } /** * g_application_invoke_action: * @application: a #GApplication * @name: the name of the action to invoke * @platform_data: (allow-none): platform-specific event data * * Invokes the action @name of the passed #GApplication. * * This function has different behavior depending on whether @application * is acting as a proxy for another process. In the normal case where * the current process is hosting the application, and the specified * action exists and is enabled, the #GApplication::action signal will * be emitted. * * If @application is a proxy, then the specified action will be invoked * in the remote process. It is not necessary to call * g_application_add_action() in the current process in order to invoke * one remotely. * * Since: 2.26 */ void g_application_invoke_action (GApplication *application, const gchar *name, GVariant *platform_data) { GApplicationPrivate *priv; GApplicationAction *action; g_return_if_fail (G_IS_APPLICATION (application)); g_return_if_fail (name != NULL); g_return_if_fail (platform_data == NULL || g_variant_is_of_type (platform_data, G_VARIANT_TYPE ("a{sv}"))); if (platform_data == NULL) platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); else g_variant_ref (platform_data); priv = application->priv; if (priv->is_remote) { _g_application_platform_remote_invoke_action (application, name, platform_data); goto out; } action = g_hash_table_lookup (priv->actions, name); g_return_if_fail (action != NULL); if (!action->enabled) goto out; g_signal_emit (application, application_signals[ACTION_WITH_DATA], g_quark_from_string (name), name, platform_data); out: g_variant_unref (platform_data); } /** * g_application_list_actions: * @application: a #GApplication * * Retrieves the list of action names currently exported by @application. * * It is an error to call this function if @application is a proxy for * a remote application. * * Return value: (transfer full): a newly allocation, %NULL-terminated array * of strings containing action names; use g_strfreev() to free the * resources used by the returned array * * Since: 2.26 */ gchar ** g_application_list_actions (GApplication *application) { GApplicationPrivate *priv; GHashTableIter iter; gpointer key; gchar **retval; gint i; g_return_val_if_fail (G_IS_APPLICATION (application), NULL); g_return_val_if_fail (!application->priv->is_remote, NULL); priv = application->priv; retval = g_new (gchar*, g_hash_table_size (priv->actions) + 1); i = 0; g_hash_table_iter_init (&iter, priv->actions); while (g_hash_table_iter_next (&iter, &key, NULL)) retval[i++] = g_strdup (key); retval[i] = NULL; return retval; } /** * g_application_set_action_enabled: * @application: a #GApplication * @name: the name of the application * @enabled: whether to enable or disable the action @name * * Sets whether the action @name inside @application should be enabled * or disabled. * * It is an error to call this function if @application is a proxy for * a remote application. * * Invoking a disabled action will not result in the #GApplication::action * signal being emitted. * * Since: 2.26 */ void g_application_set_action_enabled (GApplication *application, const gchar *name, gboolean enabled) { GApplicationAction *action; g_return_if_fail (G_IS_APPLICATION (application)); g_return_if_fail (name != NULL); g_return_if_fail (!application->priv->is_remote); enabled = !!enabled; action = g_hash_table_lookup (application->priv->actions, name); g_return_if_fail (action != NULL); if (action->enabled == enabled) return; action->enabled = enabled; queue_actions_change_notification (application); } /** * g_application_get_action_description: * @application: a #GApplication * @name: Action name * * Gets the description of the action @name. * * It is an error to call this function if @application is a proxy for * a remote application. * * Returns: Description for the given action named @name * * Since: 2.26 */ G_CONST_RETURN gchar * g_application_get_action_description (GApplication *application, const gchar *name) { GApplicationAction *action; g_return_val_if_fail (G_IS_APPLICATION (application), NULL); g_return_val_if_fail (name != NULL, NULL); g_return_val_if_fail (!application->priv->is_remote, NULL); action = g_hash_table_lookup (application->priv->actions, name); g_return_val_if_fail (action != NULL, NULL); return action->description; } /** * g_application_get_action_enabled: * @application: a #GApplication * @name: the name of the action * * Retrieves whether the action @name is enabled or not. * * See g_application_set_action_enabled(). * * It is an error to call this function if @application is a proxy for * a remote application. * * Return value: %TRUE if the action was enabled, and %FALSE otherwise * * Since: 2.26 */ gboolean g_application_get_action_enabled (GApplication *application, const gchar *name) { GApplicationAction *action; g_return_val_if_fail (G_IS_APPLICATION (application), FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (!application->priv->is_remote, FALSE); action = g_hash_table_lookup (application->priv->actions, name); g_return_val_if_fail (action != NULL, FALSE); return action->enabled; } /** * g_application_run: * @application: a #GApplication * * Starts the application. * * The default implementation of this virtual function will simply run * a main loop. * * It is an error to call this function if @application is a proxy for * a remote application. * * Since: 2.26 */ void g_application_run (GApplication *application) { g_return_if_fail (G_IS_APPLICATION (application)); g_return_if_fail (!application->priv->is_remote); G_APPLICATION_GET_CLASS (application)->run (application); } /** * g_application_quit_with_data: * @application: a #GApplication * @platform_data: (allow-none): platform-specific data * * Request that the application quits. * * This function has different behavior depending on whether @application * is acting as a proxy for another process. In the normal case where * the current process is hosting the application, the default * implementation will quit the main loop created by g_application_run(). * * If @application is a proxy, then the remote process will be asked * to quit. * * Returns: %TRUE if the application accepted the request, %FALSE otherwise * * Since: 2.26 */ gboolean g_application_quit_with_data (GApplication *application, GVariant *platform_data) { gboolean retval = FALSE; g_return_val_if_fail (G_IS_APPLICATION (application), FALSE); g_return_val_if_fail (platform_data == NULL || g_variant_is_of_type (platform_data, G_VARIANT_TYPE ("a{sv}")), FALSE); if (platform_data == NULL) platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); else g_variant_ref (platform_data); if (application->priv->is_remote) { _g_application_platform_remote_quit (application, platform_data); retval = TRUE; } else g_signal_emit (application, application_signals[QUIT_WITH_DATA], 0, platform_data, &retval); g_variant_unref (platform_data); return retval; } /** * g_application_get_instance: * * In the normal case where there is exactly one #GApplication instance * in this process, return that instance. If there are multiple, the * first one created will be returned. Otherwise, return %NULL. * * Returns: (transfer none): The primary instance of #GApplication, * or %NULL if none is set * * Since: 2.26 */ GApplication * g_application_get_instance (void) { return primary_application; } /** * g_application_get_id: * @application: a #GApplication * * Retrieves the platform-specific identifier for the #GApplication. * * Return value: The platform-specific identifier. The returned string * is owned by the #GApplication instance and it should never be * modified or freed * * Since: 2.26 */ G_CONST_RETURN gchar * g_application_get_id (GApplication *application) { g_return_val_if_fail (G_IS_APPLICATION (application), NULL); return application->priv->appid; } /** * g_application_is_remote: * @application: a #GApplication * * Returns whether the object represents a proxy for a remote application. * * Returns: %TRUE if this object represents a proxy for a remote application. */ gboolean g_application_is_remote (GApplication *application) { g_return_val_if_fail (G_IS_APPLICATION (application), FALSE); return application->priv->is_remote; } static void g_application_init (GApplication *app) { app->priv = G_TYPE_INSTANCE_GET_PRIVATE (app, G_TYPE_APPLICATION, GApplicationPrivate); app->priv->actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_application_action_free); app->priv->default_quit = TRUE; app->priv->do_register = TRUE; app->priv->is_remote = TRUE; app->priv->platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0); } static void g_application_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GApplication *app = G_APPLICATION (object); switch (prop_id) { case PROP_APPLICATION_ID: g_value_set_string (value, g_application_get_id (app)); break; case PROP_DEFAULT_QUIT: g_value_set_boolean (value, app->priv->default_quit); break; case PROP_IS_REMOTE: g_value_set_boolean (value, g_application_is_remote (app)); break; case PROP_REGISTER: g_value_set_boolean (value, app->priv->do_register); break; case PROP_ARGV: g_value_set_variant (value, app->priv->argv); break; case PROP_PLATFORM_DATA: g_value_set_variant (value, app->priv->platform_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void g_application_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GApplication *app = G_APPLICATION (object); switch (prop_id) { case PROP_APPLICATION_ID: g_return_if_fail (_g_application_validate_id (g_value_get_string (value))); app->priv->appid = g_value_dup_string (value); break; case PROP_DEFAULT_QUIT: app->priv->default_quit = g_value_get_boolean (value); break; case PROP_REGISTER: app->priv->do_register = g_value_get_boolean (value); /* If we're not registering, the default_quit no longer applies */ if (!app->priv->do_register) app->priv->default_quit = FALSE; break; case PROP_ARGV: app->priv->argv = g_value_dup_variant (value); break; case PROP_PLATFORM_DATA: { GVariant *platform_data = g_value_get_variant (value); if (app->priv->platform_data) g_variant_unref (app->priv->platform_data); app->priv->platform_data = g_variant_ref_sink (append_cwd_to_platform_data (platform_data)); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static GObject* g_application_constructor (GType type, guint n_construct_properties, GObjectConstructParam *construct_params) { GApplication *app; GObject *object; const char *appid = NULL; guint i; for (i = 0; i < n_construct_properties; i++) { GObjectConstructParam *param = &construct_params[i]; if (strcmp (param->pspec->name, "application-id") == 0) appid = g_value_get_string (param->value); } g_return_val_if_fail (appid != NULL, NULL); app = application_for_appid (appid); if (app != NULL) return g_object_ref (app); object = (* G_OBJECT_CLASS (g_application_parent_class)->constructor) (type, n_construct_properties, construct_params); app = G_APPLICATION (object); if (primary_application == NULL) primary_application = app; g_hash_table_insert (instances_for_appid, g_strdup (appid), app); return object; } static void g_application_finalize (GObject *object) { GApplication *app = G_APPLICATION (object); g_free (app->priv->appid); if (app->priv->actions) g_hash_table_unref (app->priv->actions); if (app->priv->actions_changed_id) g_source_remove (app->priv->actions_changed_id); if (app->priv->mainloop) g_main_loop_unref (app->priv->mainloop); #ifdef G_OS_UNIX g_free (app->priv->dbus_path); if (app->priv->session_bus) g_object_unref (app->priv->session_bus); #endif G_OBJECT_CLASS (g_application_parent_class)->finalize (object); } static void g_application_class_init (GApplicationClass *klass) { GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (GApplicationPrivate)); gobject_class->constructor = g_application_constructor; gobject_class->set_property = g_application_set_property; gobject_class->get_property = g_application_get_property; gobject_class->finalize = g_application_finalize; klass->run = g_application_default_run; klass->quit_with_data = g_application_default_quit_with_data; /** * GApplication::quit-with-data: * @application: the object on which the signal is emitted * @platform_data: Platform-specific data, or %NULL * * This signal is emitted when the Quit action is invoked on the * application. * * The default handler for this signal exits the mainloop of the * application. * * Returns: %TRUE if the signal has been handled, %FALSE to continue * signal emission */ application_signals[QUIT_WITH_DATA] = g_signal_new (g_intern_static_string ("quit-with-data"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GApplicationClass, quit_with_data), g_signal_accumulator_true_handled, NULL, _gio_marshal_BOOLEAN__VARIANT, G_TYPE_BOOLEAN, 1, G_TYPE_VARIANT); /** * GApplication::action-with-data: * @application: the object on which the signal is emitted * @name: The name of the activated action * @platform_data: Platform-specific data, or %NULL * * This signal is emitted when an action is activated. The action name * is passed as the first argument, but also as signal detail, so it * is possible to connect to this signal for individual actions. * * The signal is never emitted for disabled actions. */ application_signals[ACTION_WITH_DATA] = g_signal_new (g_intern_static_string ("action-with-data"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED, G_STRUCT_OFFSET (GApplicationClass, action_with_data), NULL, NULL, _gio_marshal_VOID__STRING_VARIANT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_VARIANT); /** * GApplication::prepare-activation: * @application: the object on which the signal is emitted * @arguments: A #GVariant with the signature "aay" * @platform_data: A #GVariant with the signature "a{sv}", or %NULL * * This signal is emitted when a non-primary process for a given * application is invoked while your application is running; for * example, when a file browser launches your program to open a * file. The raw operating system arguments are passed in the * @arguments variant. Additional platform-dependent data is * stored in @platform_data. */ application_signals[PREPARE_ACTIVATION] = g_signal_new (g_intern_static_string ("prepare-activation"), G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GApplicationClass, prepare_activation), NULL, NULL, _gio_marshal_VOID__VARIANT_VARIANT, G_TYPE_NONE, 2, G_TYPE_VARIANT, G_TYPE_VARIANT); /** * GApplication:application-id: * * The unique identifier for this application. See the documentation for * #GApplication for more information about this property. * */ g_object_class_install_property (gobject_class, PROP_APPLICATION_ID, g_param_spec_string ("application-id", P_("Application ID"), P_("Identifier for this application"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GApplication:argv: * * The argument vector given to this application. It must be a #GVariant * with a type signature "aay". * */ g_object_class_install_property (gobject_class, PROP_ARGV, g_param_spec_variant ("argv", P_("Argument vector"), P_("System argument vector with type signature aay"), G_VARIANT_TYPE ("aay"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GApplication:platform-data: * * Platform-specific data retrieved from the operating system * environment. It must be a #GVariant with type signature "a{sv}". * */ g_object_class_install_property (gobject_class, PROP_PLATFORM_DATA, g_param_spec_variant ("platform-data", P_("Platform data"), P_("Environmental data, must have type signature a{sv}"), G_VARIANT_TYPE ("a{sv}"), NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GApplication:default-quit: * * By default, if the GApplication:register property is %TRUE, and a * different process is running this application, the process will * be exited. Set this property to %FALSE to allow custom * interaction with the remote process. * */ g_object_class_install_property (gobject_class, PROP_DEFAULT_QUIT, g_param_spec_boolean ("default-quit", P_("Default Quit"), P_("Exit the process by default"), TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /** * GApplication:is-remote: * * This property is %TRUE if this application instance represents a proxy * to the instance of this application in another process. * */ g_object_class_install_property (gobject_class, PROP_IS_REMOTE, g_param_spec_boolean ("is-remote", P_("Is Remote"), P_("Whether this application is a proxy for another process"), TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); /** * GApplication:register: * * If this property is %FALSE, the application construction will not attempt * to ensure process uniqueness, and the application is guaranteed to be in the * remote state. See GApplication:is-remote. */ g_object_class_install_property (gobject_class, PROP_REGISTER, g_param_spec_boolean ("register", P_("Register"), P_("If false, do not "), TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); }