X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgappinfo.c;h=df0cfe0cd8ba02a0c65a579cd36156a4fcf8abbd;hb=4f775b7b7ff38581b110ce1664451381ca3d88b6;hp=b57b39500c3496dccbbc41209df6833e94fb1975;hpb=f0749cc896db4174609da7da178d079aaf758845;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gappinfo.c b/gio/gappinfo.c index b57b395..df0cfe0 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -13,73 +13,82 @@ * 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. + * Public License along with this library; if not, see . * * Author: Alexander Larsson */ -#include +#include "config.h" + #include "gappinfo.h" +#include "gappinfoprivate.h" + #include "glibintl.h" #include +#include -#include "gioalias.h" /** * SECTION:gappinfo * @short_description: Application information and launch contexts - * @include: gio.h + * @include: gio/gio.h + * @see_also: #GAppInfoMonitor * - * #GAppInfo and #GAppLaunchContext are used for describing and launching - * applications installed on the system. + * #GAppInfo and #GAppLaunchContext are used for describing and launching + * applications installed on the system. * - **/ - -static void g_app_info_base_init (gpointer g_class); -static void g_app_info_class_init (gpointer g_class, - gpointer class_data); - - -GType -g_app_info_get_type (void) -{ - static GType app_info_type = 0; - - if (! app_info_type) - { - static const GTypeInfo app_info_info = - { - sizeof (GAppInfoIface), /* class_size */ - g_app_info_base_init, /* base_init */ - NULL, /* base_finalize */ - g_app_info_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; - - app_info_type = - g_type_register_static (G_TYPE_INTERFACE, I_("GAppInfo"), - &app_info_info, 0); - - g_type_interface_add_prerequisite (app_info_type, G_TYPE_OBJECT); - } - - return app_info_type; -} + * As of GLib 2.20, URIs will always be converted to POSIX paths + * (using g_file_get_path()) when using g_app_info_launch() even if + * the application requested an URI and not a POSIX path. For example + * for an desktop-file based application with Exec key `totem + * %U` and a single URI, `sftp://foo/file.avi`, then + * `/home/user/.gvfs/sftp on foo/file.avi` will be passed. This will + * only work if a set of suitable GIO extensions (such as gvfs 2.26 + * compiled with FUSE support), is available and operational; if this + * is not the case, the URI will be passed unmodified to the application. + * Some URIs, such as `mailto:`, of course cannot be mapped to a POSIX + * path (in gvfs there's no FUSE mount for it); such URIs will be + * passed unmodified to the application. + * + * Specifically for gvfs 2.26 and later, the POSIX URI will be mapped + * back to the GIO URI in the #GFile constructors (since gvfs + * implements the #GVfs extension point). As such, if the application + * needs to examine the URI, it needs to use g_file_get_uri() or + * similar on #GFile. In other words, an application cannot assume + * that the URI passed to e.g. g_file_new_for_commandline_arg() is + * equal to the result of g_file_get_uri(). The following snippet + * illustrates this: + * + * |[ + * GFile *f; + * char *uri; + * + * file = g_file_new_for_commandline_arg (uri_from_commandline); + * + * uri = g_file_get_uri (file); + * strcmp (uri, uri_from_commandline) == 0; + * g_free (uri); + * + * if (g_file_has_uri_scheme (file, "cdda")) + * { + * // do something special with uri + * } + * g_object_unref (file); + * ]| + * + * This code will work when both `cdda://sr0/Track 1.wav` and + * `/home/user/.gvfs/cdda on sr0/Track 1.wav` is passed to the + * application. It should be noted that it's generally not safe + * for applications to rely on the format of a particular URIs. + * Different launcher applications (e.g. file managers) may have + * different ideas of what a given URI means. + */ -static void -g_app_info_class_init (gpointer g_class, - gpointer class_data) -{ -} +typedef GAppInfoIface GAppInfoInterface; +G_DEFINE_INTERFACE (GAppInfo, g_app_info, G_TYPE_OBJECT) static void -g_app_info_base_init (gpointer g_class) +g_app_info_default_init (GAppInfoInterface *iface) { } @@ -90,7 +99,7 @@ g_app_info_base_init (gpointer g_class) * * Creates a duplicate of a #GAppInfo. * - * Returns: a duplicate of @appinfo. + * Returns: (transfer full): a duplicate of @appinfo. **/ GAppInfo * g_app_info_dup (GAppInfo *appinfo) @@ -106,9 +115,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 #GAppInfos are equal. * * Returns: %TRUE if @appinfo1 is equal to @appinfo2. %FALSE otherwise. @@ -136,7 +145,7 @@ g_app_info_equal (GAppInfo *appinfo1, * * Gets the ID of an application. An id is a string that * identifies the application. The exact format of the id is - * platform dependent. For instance on Unix this is the + * platform dependent. For instance, on Unix this is the * desktop file id from the xdg menu specification. * * Note that the returned ID may be %NULL, depending on how @@ -177,6 +186,33 @@ g_app_info_get_name (GAppInfo *appinfo) } /** + * g_app_info_get_display_name: + * @appinfo: a #GAppInfo. + * + * Gets the display name of the application. The display name is often more + * descriptive to the user than the name itself. + * + * Returns: the display name of the application for @appinfo, or the name if + * no display name is available. + * + * Since: 2.24 + **/ +const char * +g_app_info_get_display_name (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->get_display_name == NULL) + return (* iface->get_name) (appinfo); + + return (* iface->get_display_name) (appinfo); +} + +/** * g_app_info_get_description: * @appinfo: a #GAppInfo. * @@ -199,12 +235,12 @@ g_app_info_get_description (GAppInfo *appinfo) /** * g_app_info_get_executable: - * @appinfo: a #GAppInfo. + * @appinfo: a #GAppInfo * * Gets the executable's name for the installed application. * * Returns: a string containing the @appinfo's application - * binary's name. + * binaries name **/ const char * g_app_info_get_executable (GAppInfo *appinfo) @@ -220,6 +256,33 @@ g_app_info_get_executable (GAppInfo *appinfo) /** + * g_app_info_get_commandline: + * @appinfo: a #GAppInfo + * + * Gets the commandline with which the application will be + * started. + * + * Returns: a string containing the @appinfo's commandline, + * or %NULL if this information is not available + * + * Since: 2.20 + **/ +const char * +g_app_info_get_commandline (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->get_commandline) + return (* iface->get_commandline) (appinfo); + + return NULL; +} + +/** * g_app_info_set_as_default_for_type: * @appinfo: a #GAppInfo. * @content_type: the content type. @@ -244,6 +307,33 @@ g_app_info_set_as_default_for_type (GAppInfo *appinfo, return (* iface->set_as_default_for_type) (appinfo, content_type, error); } +/** + * g_app_info_set_as_last_used_for_type: + * @appinfo: a #GAppInfo. + * @content_type: the content type. + * @error: a #GError. + * + * Sets the application as the last used application for a given type. + * This will make the application appear as first in the list returned + * by g_app_info_get_recommended_for_type(), regardless of the default + * application for that content type. + * + * Returns: %TRUE on success, %FALSE on error. + **/ +gboolean +g_app_info_set_as_last_used_for_type (GAppInfo *appinfo, + const char *content_type, + GError **error) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE); + g_return_val_if_fail (content_type != NULL, FALSE); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + return (* iface->set_as_last_used_for_type) (appinfo, content_type, error); +} /** * g_app_info_set_as_default_for_extension: @@ -251,7 +341,7 @@ g_app_info_set_as_default_for_type (GAppInfo *appinfo, * @extension: a string containing the file extension (without the dot). * @error: a #GError. * - * Sets the application as the default handler for the given file extention. + * Sets the application as the default handler for the given file extension. * * Returns: %TRUE on success, %FALSE on error. **/ @@ -270,7 +360,8 @@ g_app_info_set_as_default_for_extension (GAppInfo *appinfo, if (iface->set_as_default_for_extension) return (* iface->set_as_default_for_extension) (appinfo, extension, error); - g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "g_app_info_set_as_default_for_extension not supported yet"); + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + "g_app_info_set_as_default_for_extension not supported yet"); return FALSE; } @@ -301,9 +392,9 @@ g_app_info_add_supports_type (GAppInfo *appinfo, if (iface->add_supports_type) return (* iface->add_supports_type) (appinfo, content_type, error); - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "g_app_info_add_supports_type not supported yet"); + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "g_app_info_add_supports_type not supported yet"); return FALSE; } @@ -359,13 +450,44 @@ g_app_info_remove_supports_type (GAppInfo *appinfo, if (iface->remove_supports_type) return (* iface->remove_supports_type) (appinfo, content_type, error); - g_set_error (error, G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - "g_app_info_remove_supports_type not supported yet"); + g_set_error_literal (error, G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "g_app_info_remove_supports_type not supported yet"); return FALSE; } +/** + * g_app_info_get_supported_types: + * @appinfo: a #GAppInfo that can handle files + * + * Retrieves the list of content types that @app_info claims to support. + * If this information is not provided by the environment, this function + * will return %NULL. + * This function does not take in consideration associations added with + * g_app_info_add_supports_type(), but only those exported directly by + * the application. + * + * Returns: (transfer none) (array zero-terminated=1) (element-type utf8): + * a list of content types. + * + * Since: 2.34 + */ +const char ** +g_app_info_get_supported_types (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->get_supported_types) + return iface->get_supported_types (appinfo); + else + return NULL; +} + /** * g_app_info_get_icon: @@ -373,7 +495,8 @@ g_app_info_remove_supports_type (GAppInfo *appinfo, * * Gets the icon for the application. * - * Returns: the default #GIcon for @appinfo. + * Returns: (transfer none): the default #GIcon for @appinfo or %NULL + * if there is no default icon. **/ GIcon * g_app_info_get_icon (GAppInfo *appinfo) @@ -390,23 +513,40 @@ g_app_info_get_icon (GAppInfo *appinfo) /** * g_app_info_launch: - * @appinfo: a #GAppInfo. - * @files: a #GList of #GFile objects. - * @launch_context: a #GAppLaunchContext. - * @error: a #GError. + * @appinfo: a #GAppInfo + * @files: (allow-none) (element-type GFile): a #GList of #GFile objects + * @launch_context: (allow-none): 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. * - * To lauch the application without arguments pass a %NULL @files list. + * To launch the application without arguments pass a %NULL @files list. * * Note that even if the launch is successful the application launched * 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. + * Some URIs can be changed when passed through a GFile (for instance + * 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. + * + * The launched application inherits the environment of the launching + * process, but it can be modified with g_app_launch_context_setenv() + * and g_app_launch_context_unsetenv(). + * + * On UNIX, this function sets the `GIO_LAUNCHED_DESKTOP_FILE` + * environment variable with the path of the launched desktop file and + * `GIO_LAUNCHED_DESKTOP_FILE_PID` to the process id of the launched + * process. This can be used to ignore `GIO_LAUNCHED_DESKTOP_FILE`, + * should it be inherited by further processes. The `DISPLAY` and + * `DESKTOP_STARTUP_ID` 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, @@ -468,23 +608,23 @@ g_app_info_supports_files (GAppInfo *appinfo) /** * g_app_info_launch_uris: - * @appinfo: a #GAppInfo. - * @uris: a #GList containing URIs to launch. - * @launch_context: a #GAppLaunchContext. - * @error: a #GError. + * @appinfo: a #GAppInfo + * @uris: (allow-none) (element-type utf8): a #GList containing URIs to launch. + * @launch_context: (allow-none): a #GAppLaunchContext or %NULL + * @error: a #GError * - * Launches the application. Passes @uris to the launched application + * Launches the application. This passes the @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. * - * To lauch the application without arguments pass a %NULL @uris list. + * To launch the application without arguments pass a %NULL @uris list. * * Note that even if the launch is successful the application launched * 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, @@ -498,7 +638,7 @@ g_app_info_launch_uris (GAppInfo *appinfo, iface = G_APP_INFO_GET_IFACE (appinfo); - return (* iface->launch) (appinfo, uris, launch_context, error); + return (* iface->launch_uris) (appinfo, uris, launch_context, error); } @@ -523,7 +663,136 @@ g_app_info_should_show (GAppInfo *appinfo) return (* iface->should_show) (appinfo); } -G_DEFINE_TYPE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT); +/** + * g_app_info_launch_default_for_uri: + * @uri: the uri to show + * @launch_context: (allow-none): an optional #GAppLaunchContext. + * @error: a #GError. + * + * 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. + * + * Returns: %TRUE on success, %FALSE on error. + **/ +gboolean +g_app_info_launch_default_for_uri (const char *uri, + GAppLaunchContext *launch_context, + GError **error) +{ + char *uri_scheme; + GAppInfo *app_info = NULL; + GList l; + gboolean res; + + /* g_file_query_default_handler() calls + * g_app_info_get_default_for_uri_scheme() too, but we have to do it + * here anyway in case GFile can't parse @uri correctly. + */ + uri_scheme = g_uri_parse_scheme (uri); + if (uri_scheme && uri_scheme[0] != '\0') + app_info = g_app_info_get_default_for_uri_scheme (uri_scheme); + g_free (uri_scheme); + + if (!app_info) + { + GFile *file; + + file = g_file_new_for_uri (uri); + app_info = g_file_query_default_handler (file, NULL, error); + g_object_unref (file); + if (app_info == NULL) + return FALSE; + + /* We still use the original @uri rather than calling + * g_file_get_uri(), because GFile might have modified the URI + * in ways we don't want (eg, removing the fragment identifier + * from a file: URI). + */ + } + + l.data = (char *)uri; + l.next = l.prev = NULL; + res = g_app_info_launch_uris (app_info, &l, + launch_context, error); + + g_object_unref (app_info); + + return res; +} + +/** + * g_app_info_can_delete: + * @appinfo: a #GAppInfo + * + * Obtains the information whether the #GAppInfo can be deleted. + * See g_app_info_delete(). + * + * Returns: %TRUE if @appinfo can be deleted + * + * Since: 2.20 + */ +gboolean +g_app_info_can_delete (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->can_delete) + return (* iface->can_delete) (appinfo); + + return FALSE; +} + + +/** + * g_app_info_delete: + * @appinfo: a #GAppInfo + * + * Tries to delete a #GAppInfo. + * + * On some platforms, there may be a difference between user-defined + * #GAppInfos which can be deleted, and system-wide ones which cannot. + * See g_app_info_can_delete(). + * + * Virtual: do_delete + * Returns: %TRUE if @appinfo has been deleted + * + * Since: 2.20 + */ +gboolean +g_app_info_delete (GAppInfo *appinfo) +{ + GAppInfoIface *iface; + + g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE); + + iface = G_APP_INFO_GET_IFACE (appinfo); + + if (iface->do_delete) + return (* iface->do_delete) (appinfo); + + return FALSE; +} + + +enum { + LAUNCH_FAILED, + LAUNCHED, + LAST_SIGNAL +}; + +struct _GAppLaunchContextPrivate { + char **envp; +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE_WITH_PRIVATE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT) /** * g_app_launch_context_new: @@ -540,27 +809,148 @@ g_app_launch_context_new (void) } static void +g_app_launch_context_finalize (GObject *object) +{ + GAppLaunchContext *context = G_APP_LAUNCH_CONTEXT (object); + + g_strfreev (context->priv->envp); + + G_OBJECT_CLASS (g_app_launch_context_parent_class)->finalize (object); +} + +static void g_app_launch_context_class_init (GAppLaunchContextClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = g_app_launch_context_finalize; + + /* + * GAppLaunchContext::launch-failed: + * @context: the object emitting the signal + * @startup_notify_id: the startup notification id for the failed launch + * + * The ::launch-failed signal is emitted when a #GAppInfo launch + * fails. The startup notification id is provided, so that the launcher + * can cancel the startup notification. + * + * Since: 2.36 + */ + signals[LAUNCH_FAILED] = g_signal_new ("launch-failed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GAppLaunchContextClass, launch_failed), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_STRING); + + /* + * GAppLaunchContext::launched: + * @context: the object emitting the signal + * @info: the #GAppInfo that was just launched + * @platform_data: additional platform-specific data for this launch + * + * The ::launched signal is emitted when a #GAppInfo is successfully + * launched. The @platform_data is an GVariant dictionary mapping + * strings to variants (ie a{sv}), which contains additional, + * platform-specific data about this launch. On UNIX, at least the + * "pid" and "startup-notification-id" keys will be present. + * + * Since: 2.36 + */ + signals[LAUNCHED] = g_signal_new ("launched", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GAppLaunchContextClass, launched), + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_APP_INFO, G_TYPE_VARIANT); } static void -g_app_launch_context_init (GAppLaunchContext *launch_context) +g_app_launch_context_init (GAppLaunchContext *context) +{ + context->priv = g_app_launch_context_get_instance_private (context); +} + +/** + * g_app_launch_context_setenv: + * @context: a #GAppLaunchContext + * @variable: the environment variable to set + * @value: the value for to set the variable to. + * + * Arranges for @variable to be set to @value in the child's + * environment when @context is used to launch an application. + * + * Since: 2.32 + */ +void +g_app_launch_context_setenv (GAppLaunchContext *context, + const char *variable, + const char *value) +{ + if (!context->priv->envp) + context->priv->envp = g_get_environ (); + + context->priv->envp = + g_environ_setenv (context->priv->envp, variable, value, TRUE); +} + +/** + * g_app_launch_context_unsetenv: + * @context: a #GAppLaunchContext + * @variable: the environment variable to remove + * + * Arranges for @variable to be unset in the child's environment + * when @context is used to launch an application. + * + * Since: 2.32 + */ +void +g_app_launch_context_unsetenv (GAppLaunchContext *context, + const char *variable) { + if (!context->priv->envp) + context->priv->envp = g_get_environ (); + + context->priv->envp = + g_environ_unsetenv (context->priv->envp, variable); +} + +/** + * g_app_launch_context_get_environment: + * @context: a #GAppLaunchContext + * + * Gets the complete environment variable list to be passed to + * the child process when @context is used to launch an application. + * This is a %NULL-terminated array of strings, where each string has + * the form `KEY=VALUE`. + * + * Returns: (array zero-terminated=1) (transfer full): the + * child's environment + * + * Since: 2.32 + */ +char ** +g_app_launch_context_get_environment (GAppLaunchContext *context) +{ + if (!context->priv->envp) + context->priv->envp = g_get_environ (); + + return g_strdupv (context->priv->envp); } /** * g_app_launch_context_get_display: - * @context: a #GAppLaunchContext. - * @info: a #GAppInfo. - * @files: a #GList of files. + * @context: a #GAppLaunchContext + * @info: a #GAppInfo + * @files: (element-type GFile): a #GList of #GFile objects + * + * 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 `DISPLAY` environment variable. * - * Gets the display string for the display. This is used to ensure new - * applications are started on the same display as the launching - * application. - * * Returns: a display string for the display. - **/ + */ char * g_app_launch_context_get_display (GAppLaunchContext *context, GAppInfo *info, @@ -581,18 +971,17 @@ g_app_launch_context_get_display (GAppLaunchContext *context, /** * g_app_launch_context_get_startup_notify_id: - * @context: a #GAppLaunchContext. - * @info: a #GAppInfo. - * @files: a #GList of files. + * @context: a #GAppLaunchContext + * @info: a #GAppInfo + * @files: (element-type GFile): a #GList of of #GFile objects * - * Initiates startup notification for the applicaiont and returns the - * DESKTOP_STARTUP_ID for the launched operation, if supported. + * Initiates startup notification for the application and returns the + * `DESKTOP_STARTUP_ID` for the launched operation, if supported. * - * Startup notification IDs are defined in the FreeDesktop.Org Startup - * Notifications standard, at - * . + * Startup notification IDs are defined in the + * [FreeDesktop.Org Startup Notifications standard](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt"). * - * 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 * @@ -627,16 +1016,233 @@ void g_app_launch_context_launch_failed (GAppLaunchContext *context, const char *startup_notify_id) { - GAppLaunchContextClass *class; - g_return_if_fail (G_IS_APP_LAUNCH_CONTEXT (context)); g_return_if_fail (startup_notify_id != NULL); - class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context); + g_signal_emit (context, signals[LAUNCH_FAILED], 0, startup_notify_id); +} - if (class->launch_failed != NULL) - class->launch_failed (context, startup_notify_id); + +/** + * SECTION:gappinfomonitor + * @short_description: Monitor application information for changes + * + * #GAppInfoMonitor is a very simple object used for monitoring the app + * info database for changes (ie: newly installed or removed + * applications). + * + * Call g_app_info_monitor_get() to get a #GAppInfoMonitor and connect + * to the "changed" signal. + * + * In the usual case, applications should try to make note of the change + * (doing things like invalidating caches) but not act on it. In + * particular, applications should avoid making calls to #GAppInfo APIs + * in response to the change signal, deferring these until the time that + * the data is actually required. The exception to this case is when + * application information is actually being displayed on the screen + * (eg: during a search or when the list of all applications is shown). + * The reason for this is that changes to the list of installed + * applications often come in groups (like during system updates) and + * rescanning the list on every change is pointless and expensive. + * + * Since: 2.40 + **/ + +/** + * GAppInfoMonitor: + * + * The only thing you can do with this is to get it via + * g_app_info_monitor_get() and connect to the "changed" signal. + * + * Since: 2.40 + **/ + +/* We have one of each of these per main context and hand them out + * according to the thread default main context at the time of the call + * to g_app_info_monitor_get(). + * + * g_object_unref() is only ever called from the same context, so we + * effectively have a single-threaded scenario for each GAppInfoMonitor. + * + * We use a hashtable to cache the per-context monitor (but we do not + * hold a ref). During finalize, we remove it. This is possible + * because we don't have to worry about the usual races due to the + * single-threaded nature of each object. + * + * We keep a global list of all contexts that have a monitor for them, + * which we have to access under a lock. When we dispatch the events to + * be handled in each context, we don't pass the monitor, but the + * context itself. + * + * We dispatch from the GLib worker context, so if we passed the + * monitor, we would need to take a ref on it (in case it was destroyed + * in its own thread meanwhile). The monitor holds a ref on a context + * and the dispatch would mean that the context would hold a ref on the + * monitor. If someone stopped iterating the context at just this + * moment both the context and monitor would leak. + * + * Instead, we dispatch the context to itself. We don't hold a ref. + * There is the danger that the context will be destroyed during the + * dispatch, but if that is the case then we just won't receive our + * callback. + * + * When the dispatch occurs we just lookup the monitor in the hashtable, + * by context. We can now add and remove refs, since the context will + * have been acquired. + */ + +typedef struct _GAppInfoMonitorClass GAppInfoMonitorClass; + +struct _GAppInfoMonitor +{ + GObject parent_instance; + GMainContext *context; +}; + +struct _GAppInfoMonitorClass +{ + GObjectClass parent_class; +}; + +static GHashTable *g_app_info_monitors; +static GMutex g_app_info_monitor_lock; +static guint g_app_info_monitor_changed_signal; + +G_DEFINE_TYPE (GAppInfoMonitor, g_app_info_monitor, G_TYPE_OBJECT) + +static void +g_app_info_monitor_finalize (GObject *object) +{ + GAppInfoMonitor *monitor = G_APP_INFO_MONITOR (object); + + g_mutex_lock (&g_app_info_monitor_lock); + g_hash_table_remove (g_app_info_monitors, monitor->context); + g_mutex_unlock (&g_app_info_monitor_lock); + + G_OBJECT_CLASS (g_app_info_monitor_parent_class)->finalize (object); } -#define __G_APP_INFO_C__ -#include "gioaliasdef.c" +static void +g_app_info_monitor_init (GAppInfoMonitor *monitor) +{ +} + +static void +g_app_info_monitor_class_init (GAppInfoMonitorClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_app_info_monitor_changed_signal = g_signal_new ("changed", G_TYPE_APP_INFO_MONITOR, G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + object_class->finalize = g_app_info_monitor_finalize; +} + +/** + * g_app_info_monitor_get: + * + * Gets the #GAppInfoMonitor for the current thread-default main + * context. + * + * The #GAppInfoMonitor will emit a "changed" signal in the + * thread-default main context whenever the list of installed + * applications (as reported by g_app_info_get_all()) may have changed. + * + * You must only call g_object_unref() on the return value from under + * the same main context as you created it. + * + * Returns: (transfer full): a reference to a #GAppInfoMonitor + * + * Since: 2.40 + **/ +GAppInfoMonitor * +g_app_info_monitor_get (void) +{ + GAppInfoMonitor *monitor; + GMainContext *context; + + context = g_main_context_get_thread_default (); + if (!context) + context = g_main_context_default (); + + g_return_val_if_fail (g_main_context_acquire (context), NULL); + + g_mutex_lock (&g_app_info_monitor_lock); + if (!g_app_info_monitors) + g_app_info_monitors = g_hash_table_new (NULL, NULL); + + monitor = g_hash_table_lookup (g_app_info_monitors, context); + g_mutex_unlock (&g_app_info_monitor_lock); + + if (!monitor) + { + monitor = g_object_new (G_TYPE_APP_INFO_MONITOR, NULL); + monitor->context = g_main_context_ref (context); + + g_mutex_lock (&g_app_info_monitor_lock); + g_hash_table_insert (g_app_info_monitors, context, monitor); + g_mutex_unlock (&g_app_info_monitor_lock); + } + else + g_object_ref (monitor); + + g_main_context_release (context); + + return monitor; +} + +static gboolean +g_app_info_monitor_emit (gpointer user_data) +{ + GMainContext *context = user_data; + GAppInfoMonitor *monitor; + + g_mutex_lock (&g_app_info_monitor_lock); + monitor = g_hash_table_lookup (g_app_info_monitors, context); + g_mutex_unlock (&g_app_info_monitor_lock); + + /* It is possible that the monitor was already destroyed by the time + * we get here, so make sure it's not NULL. + */ + if (monitor != NULL) + { + /* We don't have to worry about another thread disposing the + * monitor but we do have to worry about the possibility that one + * of the attached handlers may do so. + * + * Take a ref so that the monitor doesn't disappear in the middle + * of the emission. + */ + g_object_ref (monitor); + g_signal_emit (monitor, g_app_info_monitor_changed_signal, 0); + g_object_unref (monitor); + } + + return FALSE; +} + +void +g_app_info_monitor_fire (void) +{ + GHashTableIter iter; + gpointer context; + + g_mutex_lock (&g_app_info_monitor_lock); + + if (g_app_info_monitors) + { + g_hash_table_iter_init (&iter, g_app_info_monitors); + while (g_hash_table_iter_next (&iter, &context, NULL)) + { + GSource *idle; + + idle = g_idle_source_new (); + g_source_set_callback (idle, g_app_info_monitor_emit, context, NULL); + g_source_set_name (idle, "[gio] g_app_info_monitor_emit"); + g_source_attach (idle, context); + g_source_unref (idle); + } + } + + g_mutex_unlock (&g_app_info_monitor_lock); +}