• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Alexander Larsson <alexl@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include "gappinfo.h"
24 #include "gappinfoprivate.h"
25 #include "gcontextspecificgroup.h"
26 #include "gtask.h"
27 #include "gcancellable.h"
28 
29 #include "glibintl.h"
30 #include "gmarshal-internal.h"
31 #include <gioerror.h>
32 #include <gfile.h>
33 
34 #ifdef G_OS_UNIX
35 #include "gdbusconnection.h"
36 #include "gdbusmessage.h"
37 #include "gportalsupport.h"
38 #include "gunixfdlist.h"
39 #include "gopenuriportal.h"
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #endif
44 
45 /**
46  * SECTION:gappinfo
47  * @short_description: Application information and launch contexts
48  * @include: gio/gio.h
49  * @see_also: #GAppInfoMonitor
50  *
51  * #GAppInfo and #GAppLaunchContext are used for describing and launching
52  * applications installed on the system.
53  *
54  * As of GLib 2.20, URIs will always be converted to POSIX paths
55  * (using g_file_get_path()) when using g_app_info_launch() even if
56  * the application requested an URI and not a POSIX path. For example
57  * for a desktop-file based application with Exec key `totem
58  * %U` and a single URI, `sftp://foo/file.avi`, then
59  * `/home/user/.gvfs/sftp on foo/file.avi` will be passed. This will
60  * only work if a set of suitable GIO extensions (such as gvfs 2.26
61  * compiled with FUSE support), is available and operational; if this
62  * is not the case, the URI will be passed unmodified to the application.
63  * Some URIs, such as `mailto:`, of course cannot be mapped to a POSIX
64  * path (in gvfs there's no FUSE mount for it); such URIs will be
65  * passed unmodified to the application.
66  *
67  * Specifically for gvfs 2.26 and later, the POSIX URI will be mapped
68  * back to the GIO URI in the #GFile constructors (since gvfs
69  * implements the #GVfs extension point). As such, if the application
70  * needs to examine the URI, it needs to use g_file_get_uri() or
71  * similar on #GFile. In other words, an application cannot assume
72  * that the URI passed to e.g. g_file_new_for_commandline_arg() is
73  * equal to the result of g_file_get_uri(). The following snippet
74  * illustrates this:
75  *
76  * |[
77  * GFile *f;
78  * char *uri;
79  *
80  * file = g_file_new_for_commandline_arg (uri_from_commandline);
81  *
82  * uri = g_file_get_uri (file);
83  * strcmp (uri, uri_from_commandline) == 0;
84  * g_free (uri);
85  *
86  * if (g_file_has_uri_scheme (file, "cdda"))
87  *   {
88  *     // do something special with uri
89  *   }
90  * g_object_unref (file);
91  * ]|
92  *
93  * This code will work when both `cdda://sr0/Track 1.wav` and
94  * `/home/user/.gvfs/cdda on sr0/Track 1.wav` is passed to the
95  * application. It should be noted that it's generally not safe
96  * for applications to rely on the format of a particular URIs.
97  * Different launcher applications (e.g. file managers) may have
98  * different ideas of what a given URI means.
99  */
100 
101 struct _GAppLaunchContextPrivate {
102   char **envp;
103 };
104 
105 typedef GAppInfoIface GAppInfoInterface;
G_DEFINE_INTERFACE(GAppInfo,g_app_info,G_TYPE_OBJECT)106 G_DEFINE_INTERFACE (GAppInfo, g_app_info, G_TYPE_OBJECT)
107 
108 static void
109 g_app_info_default_init (GAppInfoInterface *iface)
110 {
111 }
112 
113 
114 /**
115  * g_app_info_dup:
116  * @appinfo: a #GAppInfo.
117  *
118  * Creates a duplicate of a #GAppInfo.
119  *
120  * Returns: (transfer full): a duplicate of @appinfo.
121  **/
122 GAppInfo *
g_app_info_dup(GAppInfo * appinfo)123 g_app_info_dup (GAppInfo *appinfo)
124 {
125   GAppInfoIface *iface;
126 
127   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
128 
129   iface = G_APP_INFO_GET_IFACE (appinfo);
130 
131   return (* iface->dup) (appinfo);
132 }
133 
134 /**
135  * g_app_info_equal:
136  * @appinfo1: the first #GAppInfo.
137  * @appinfo2: the second #GAppInfo.
138  *
139  * Checks if two #GAppInfos are equal.
140  *
141  * Note that the check *may not* compare each individual
142  * field, and only does an identity check. In case detecting changes in the
143  * contents is needed, program code must additionally compare relevant fields.
144  *
145  * Returns: %TRUE if @appinfo1 is equal to @appinfo2. %FALSE otherwise.
146  **/
147 gboolean
g_app_info_equal(GAppInfo * appinfo1,GAppInfo * appinfo2)148 g_app_info_equal (GAppInfo *appinfo1,
149 		  GAppInfo *appinfo2)
150 {
151   GAppInfoIface *iface;
152 
153   g_return_val_if_fail (G_IS_APP_INFO (appinfo1), FALSE);
154   g_return_val_if_fail (G_IS_APP_INFO (appinfo2), FALSE);
155 
156   if (G_TYPE_FROM_INSTANCE (appinfo1) != G_TYPE_FROM_INSTANCE (appinfo2))
157     return FALSE;
158 
159   iface = G_APP_INFO_GET_IFACE (appinfo1);
160 
161   return (* iface->equal) (appinfo1, appinfo2);
162 }
163 
164 /**
165  * g_app_info_get_id:
166  * @appinfo: a #GAppInfo.
167  *
168  * Gets the ID of an application. An id is a string that
169  * identifies the application. The exact format of the id is
170  * platform dependent. For instance, on Unix this is the
171  * desktop file id from the xdg menu specification.
172  *
173  * Note that the returned ID may be %NULL, depending on how
174  * the @appinfo has been constructed.
175  *
176  * Returns: (nullable): a string containing the application's ID.
177  **/
178 const char *
g_app_info_get_id(GAppInfo * appinfo)179 g_app_info_get_id (GAppInfo *appinfo)
180 {
181   GAppInfoIface *iface;
182 
183   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
184 
185   iface = G_APP_INFO_GET_IFACE (appinfo);
186 
187   return (* iface->get_id) (appinfo);
188 }
189 
190 /**
191  * g_app_info_get_name:
192  * @appinfo: a #GAppInfo.
193  *
194  * Gets the installed name of the application.
195  *
196  * Returns: the name of the application for @appinfo.
197  **/
198 const char *
g_app_info_get_name(GAppInfo * appinfo)199 g_app_info_get_name (GAppInfo *appinfo)
200 {
201   GAppInfoIface *iface;
202 
203   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
204 
205   iface = G_APP_INFO_GET_IFACE (appinfo);
206 
207   return (* iface->get_name) (appinfo);
208 }
209 
210 /**
211  * g_app_info_get_display_name:
212  * @appinfo: a #GAppInfo.
213  *
214  * Gets the display name of the application. The display name is often more
215  * descriptive to the user than the name itself.
216  *
217  * Returns: the display name of the application for @appinfo, or the name if
218  * no display name is available.
219  *
220  * Since: 2.24
221  **/
222 const char *
g_app_info_get_display_name(GAppInfo * appinfo)223 g_app_info_get_display_name (GAppInfo *appinfo)
224 {
225   GAppInfoIface *iface;
226 
227   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
228 
229   iface = G_APP_INFO_GET_IFACE (appinfo);
230 
231   if (iface->get_display_name == NULL)
232     return (* iface->get_name) (appinfo);
233 
234   return (* iface->get_display_name) (appinfo);
235 }
236 
237 /**
238  * g_app_info_get_description:
239  * @appinfo: a #GAppInfo.
240  *
241  * Gets a human-readable description of an installed application.
242  *
243  * Returns: (nullable): a string containing a description of the
244  * application @appinfo, or %NULL if none.
245  **/
246 const char *
g_app_info_get_description(GAppInfo * appinfo)247 g_app_info_get_description (GAppInfo *appinfo)
248 {
249   GAppInfoIface *iface;
250 
251   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
252 
253   iface = G_APP_INFO_GET_IFACE (appinfo);
254 
255   return (* iface->get_description) (appinfo);
256 }
257 
258 /**
259  * g_app_info_get_executable: (virtual get_executable)
260  * @appinfo: a #GAppInfo
261  *
262  * Gets the executable's name for the installed application.
263  *
264  * Returns: (type filename): a string containing the @appinfo's application
265  * binaries name
266  **/
267 const char *
g_app_info_get_executable(GAppInfo * appinfo)268 g_app_info_get_executable (GAppInfo *appinfo)
269 {
270   GAppInfoIface *iface;
271 
272   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
273 
274   iface = G_APP_INFO_GET_IFACE (appinfo);
275 
276   return (* iface->get_executable) (appinfo);
277 }
278 
279 
280 /**
281  * g_app_info_get_commandline: (virtual get_commandline)
282  * @appinfo: a #GAppInfo
283  *
284  * Gets the commandline with which the application will be
285  * started.
286  *
287  * Returns: (nullable) (type filename): a string containing the @appinfo's commandline,
288  *     or %NULL if this information is not available
289  *
290  * Since: 2.20
291  **/
292 const char *
g_app_info_get_commandline(GAppInfo * appinfo)293 g_app_info_get_commandline (GAppInfo *appinfo)
294 {
295   GAppInfoIface *iface;
296 
297   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
298 
299   iface = G_APP_INFO_GET_IFACE (appinfo);
300 
301   if (iface->get_commandline)
302     return (* iface->get_commandline) (appinfo);
303 
304   return NULL;
305 }
306 
307 /**
308  * g_app_info_set_as_default_for_type:
309  * @appinfo: a #GAppInfo.
310  * @content_type: the content type.
311  * @error: a #GError.
312  *
313  * Sets the application as the default handler for a given type.
314  *
315  * Returns: %TRUE on success, %FALSE on error.
316  **/
317 gboolean
g_app_info_set_as_default_for_type(GAppInfo * appinfo,const char * content_type,GError ** error)318 g_app_info_set_as_default_for_type (GAppInfo    *appinfo,
319 				    const char  *content_type,
320 				    GError     **error)
321 {
322   GAppInfoIface *iface;
323 
324   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
325   g_return_val_if_fail (content_type != NULL, FALSE);
326 
327   iface = G_APP_INFO_GET_IFACE (appinfo);
328 
329   return (* iface->set_as_default_for_type) (appinfo, content_type, error);
330 }
331 
332 /**
333  * g_app_info_set_as_last_used_for_type:
334  * @appinfo: a #GAppInfo.
335  * @content_type: the content type.
336  * @error: a #GError.
337  *
338  * Sets the application as the last used application for a given type.
339  * This will make the application appear as first in the list returned
340  * by g_app_info_get_recommended_for_type(), regardless of the default
341  * application for that content type.
342  *
343  * Returns: %TRUE on success, %FALSE on error.
344  **/
345 gboolean
g_app_info_set_as_last_used_for_type(GAppInfo * appinfo,const char * content_type,GError ** error)346 g_app_info_set_as_last_used_for_type (GAppInfo    *appinfo,
347                                       const char  *content_type,
348                                       GError     **error)
349 {
350   GAppInfoIface *iface;
351 
352   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
353   g_return_val_if_fail (content_type != NULL, FALSE);
354 
355   iface = G_APP_INFO_GET_IFACE (appinfo);
356 
357   return (* iface->set_as_last_used_for_type) (appinfo, content_type, error);
358 }
359 
360 /**
361  * g_app_info_set_as_default_for_extension:
362  * @appinfo: a #GAppInfo.
363  * @extension: (type filename): a string containing the file extension
364  *     (without the dot).
365  * @error: a #GError.
366  *
367  * Sets the application as the default handler for the given file extension.
368  *
369  * Returns: %TRUE on success, %FALSE on error.
370  **/
371 gboolean
g_app_info_set_as_default_for_extension(GAppInfo * appinfo,const char * extension,GError ** error)372 g_app_info_set_as_default_for_extension (GAppInfo    *appinfo,
373 					 const char  *extension,
374 					 GError     **error)
375 {
376   GAppInfoIface *iface;
377 
378   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
379   g_return_val_if_fail (extension != NULL, FALSE);
380 
381   iface = G_APP_INFO_GET_IFACE (appinfo);
382 
383   if (iface->set_as_default_for_extension)
384     return (* iface->set_as_default_for_extension) (appinfo, extension, error);
385 
386   g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
387                        "g_app_info_set_as_default_for_extension not supported yet");
388   return FALSE;
389 }
390 
391 
392 /**
393  * g_app_info_add_supports_type:
394  * @appinfo: a #GAppInfo.
395  * @content_type: a string.
396  * @error: a #GError.
397  *
398  * Adds a content type to the application information to indicate the
399  * application is capable of opening files with the given content type.
400  *
401  * Returns: %TRUE on success, %FALSE on error.
402  **/
403 gboolean
g_app_info_add_supports_type(GAppInfo * appinfo,const char * content_type,GError ** error)404 g_app_info_add_supports_type (GAppInfo    *appinfo,
405 			      const char  *content_type,
406 			      GError     **error)
407 {
408   GAppInfoIface *iface;
409 
410   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
411   g_return_val_if_fail (content_type != NULL, FALSE);
412 
413   iface = G_APP_INFO_GET_IFACE (appinfo);
414 
415   if (iface->add_supports_type)
416     return (* iface->add_supports_type) (appinfo, content_type, error);
417 
418   g_set_error_literal (error, G_IO_ERROR,
419                        G_IO_ERROR_NOT_SUPPORTED,
420                        "g_app_info_add_supports_type not supported yet");
421 
422   return FALSE;
423 }
424 
425 
426 /**
427  * g_app_info_can_remove_supports_type:
428  * @appinfo: a #GAppInfo.
429  *
430  * Checks if a supported content type can be removed from an application.
431  *
432  * Returns: %TRUE if it is possible to remove supported
433  *     content types from a given @appinfo, %FALSE if not.
434  **/
435 gboolean
g_app_info_can_remove_supports_type(GAppInfo * appinfo)436 g_app_info_can_remove_supports_type (GAppInfo *appinfo)
437 {
438   GAppInfoIface *iface;
439 
440   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
441 
442   iface = G_APP_INFO_GET_IFACE (appinfo);
443 
444   if (iface->can_remove_supports_type)
445     return (* iface->can_remove_supports_type) (appinfo);
446 
447   return FALSE;
448 }
449 
450 
451 /**
452  * g_app_info_remove_supports_type:
453  * @appinfo: a #GAppInfo.
454  * @content_type: a string.
455  * @error: a #GError.
456  *
457  * Removes a supported type from an application, if possible.
458  *
459  * Returns: %TRUE on success, %FALSE on error.
460  **/
461 gboolean
g_app_info_remove_supports_type(GAppInfo * appinfo,const char * content_type,GError ** error)462 g_app_info_remove_supports_type (GAppInfo    *appinfo,
463 				 const char  *content_type,
464 				 GError     **error)
465 {
466   GAppInfoIface *iface;
467 
468   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
469   g_return_val_if_fail (content_type != NULL, FALSE);
470 
471   iface = G_APP_INFO_GET_IFACE (appinfo);
472 
473   if (iface->remove_supports_type)
474     return (* iface->remove_supports_type) (appinfo, content_type, error);
475 
476   g_set_error_literal (error, G_IO_ERROR,
477                        G_IO_ERROR_NOT_SUPPORTED,
478                        "g_app_info_remove_supports_type not supported yet");
479 
480   return FALSE;
481 }
482 
483 /**
484  * g_app_info_get_supported_types:
485  * @appinfo: a #GAppInfo that can handle files
486  *
487  * Retrieves the list of content types that @app_info claims to support.
488  * If this information is not provided by the environment, this function
489  * will return %NULL.
490  * This function does not take in consideration associations added with
491  * g_app_info_add_supports_type(), but only those exported directly by
492  * the application.
493  *
494  * Returns: (transfer none) (array zero-terminated=1) (element-type utf8):
495  *    a list of content types.
496  *
497  * Since: 2.34
498  */
499 const char **
g_app_info_get_supported_types(GAppInfo * appinfo)500 g_app_info_get_supported_types (GAppInfo *appinfo)
501 {
502   GAppInfoIface *iface;
503 
504   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
505 
506   iface = G_APP_INFO_GET_IFACE (appinfo);
507 
508   if (iface->get_supported_types)
509     return iface->get_supported_types (appinfo);
510   else
511     return NULL;
512 }
513 
514 
515 /**
516  * g_app_info_get_icon:
517  * @appinfo: a #GAppInfo.
518  *
519  * Gets the icon for the application.
520  *
521  * Returns: (nullable) (transfer none): the default #GIcon for @appinfo or %NULL
522  * if there is no default icon.
523  **/
524 GIcon *
g_app_info_get_icon(GAppInfo * appinfo)525 g_app_info_get_icon (GAppInfo *appinfo)
526 {
527   GAppInfoIface *iface;
528 
529   g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
530 
531   iface = G_APP_INFO_GET_IFACE (appinfo);
532 
533   return (* iface->get_icon) (appinfo);
534 }
535 
536 
537 /**
538  * g_app_info_launch:
539  * @appinfo: a #GAppInfo
540  * @files: (nullable) (element-type GFile): a #GList of #GFile objects
541  * @context: (nullable): a #GAppLaunchContext or %NULL
542  * @error: a #GError
543  *
544  * Launches the application. Passes @files to the launched application
545  * as arguments, using the optional @context to get information
546  * about the details of the launcher (like what screen it is on).
547  * On error, @error will be set accordingly.
548  *
549  * To launch the application without arguments pass a %NULL @files list.
550  *
551  * Note that even if the launch is successful the application launched
552  * can fail to start if it runs into problems during startup. There is
553  * no way to detect this.
554  *
555  * Some URIs can be changed when passed through a GFile (for instance
556  * unsupported URIs with strange formats like mailto:), so if you have
557  * a textual URI you want to pass in as argument, consider using
558  * g_app_info_launch_uris() instead.
559  *
560  * The launched application inherits the environment of the launching
561  * process, but it can be modified with g_app_launch_context_setenv()
562  * and g_app_launch_context_unsetenv().
563  *
564  * On UNIX, this function sets the `GIO_LAUNCHED_DESKTOP_FILE`
565  * environment variable with the path of the launched desktop file and
566  * `GIO_LAUNCHED_DESKTOP_FILE_PID` to the process id of the launched
567  * process. This can be used to ignore `GIO_LAUNCHED_DESKTOP_FILE`,
568  * should it be inherited by further processes. The `DISPLAY` and
569  * `DESKTOP_STARTUP_ID` environment variables are also set, based
570  * on information provided in @context.
571  *
572  * Returns: %TRUE on successful launch, %FALSE otherwise.
573  **/
574 gboolean
g_app_info_launch(GAppInfo * appinfo,GList * files,GAppLaunchContext * launch_context,GError ** error)575 g_app_info_launch (GAppInfo           *appinfo,
576 		   GList              *files,
577 		   GAppLaunchContext  *launch_context,
578 		   GError            **error)
579 {
580   GAppInfoIface *iface;
581 
582   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
583 
584   iface = G_APP_INFO_GET_IFACE (appinfo);
585 
586   return (* iface->launch) (appinfo, files, launch_context, error);
587 }
588 
589 
590 /**
591  * g_app_info_supports_uris:
592  * @appinfo: a #GAppInfo.
593  *
594  * Checks if the application supports reading files and directories from URIs.
595  *
596  * Returns: %TRUE if the @appinfo supports URIs.
597  **/
598 gboolean
g_app_info_supports_uris(GAppInfo * appinfo)599 g_app_info_supports_uris (GAppInfo *appinfo)
600 {
601   GAppInfoIface *iface;
602 
603   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
604 
605   iface = G_APP_INFO_GET_IFACE (appinfo);
606 
607   return (* iface->supports_uris) (appinfo);
608 }
609 
610 
611 /**
612  * g_app_info_supports_files:
613  * @appinfo: a #GAppInfo.
614  *
615  * Checks if the application accepts files as arguments.
616  *
617  * Returns: %TRUE if the @appinfo supports files.
618  **/
619 gboolean
g_app_info_supports_files(GAppInfo * appinfo)620 g_app_info_supports_files (GAppInfo *appinfo)
621 {
622   GAppInfoIface *iface;
623 
624   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
625 
626   iface = G_APP_INFO_GET_IFACE (appinfo);
627 
628   return (* iface->supports_files) (appinfo);
629 }
630 
631 
632 /**
633  * g_app_info_launch_uris:
634  * @appinfo: a #GAppInfo
635  * @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
636  * @context: (nullable): a #GAppLaunchContext or %NULL
637  * @error: a #GError
638  *
639  * Launches the application. This passes the @uris to the launched application
640  * as arguments, using the optional @context to get information
641  * about the details of the launcher (like what screen it is on).
642  * On error, @error will be set accordingly.
643  *
644  * To launch the application without arguments pass a %NULL @uris list.
645  *
646  * Note that even if the launch is successful the application launched
647  * can fail to start if it runs into problems during startup. There is
648  * no way to detect this.
649  *
650  * Returns: %TRUE on successful launch, %FALSE otherwise.
651  **/
652 gboolean
g_app_info_launch_uris(GAppInfo * appinfo,GList * uris,GAppLaunchContext * launch_context,GError ** error)653 g_app_info_launch_uris (GAppInfo           *appinfo,
654 			GList              *uris,
655 			GAppLaunchContext  *launch_context,
656 			GError            **error)
657 {
658   GAppInfoIface *iface;
659 
660   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
661 
662   iface = G_APP_INFO_GET_IFACE (appinfo);
663 
664   return (* iface->launch_uris) (appinfo, uris, launch_context, error);
665 }
666 
667 /**
668  * g_app_info_launch_uris_async:
669  * @appinfo: a #GAppInfo
670  * @uris: (nullable) (element-type utf8): a #GList containing URIs to launch.
671  * @context: (nullable): a #GAppLaunchContext or %NULL
672  * @cancellable: (nullable): a #GCancellable
673  * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
674  * @user_data: (nullable): data to pass to @callback
675  *
676  * Async version of g_app_info_launch_uris().
677  *
678  * The @callback is invoked immediately after the application launch, but it
679  * waits for activation in case of D-Bus–activated applications and also provides
680  * extended error information for sandboxed applications, see notes for
681  * g_app_info_launch_default_for_uri_async().
682  *
683  * Since: 2.60
684  **/
685 void
g_app_info_launch_uris_async(GAppInfo * appinfo,GList * uris,GAppLaunchContext * context,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)686 g_app_info_launch_uris_async (GAppInfo           *appinfo,
687                               GList              *uris,
688                               GAppLaunchContext  *context,
689                               GCancellable       *cancellable,
690                               GAsyncReadyCallback callback,
691                               gpointer            user_data)
692 {
693   GAppInfoIface *iface;
694 
695   g_return_if_fail (G_IS_APP_INFO (appinfo));
696   g_return_if_fail (context == NULL || G_IS_APP_LAUNCH_CONTEXT (context));
697   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
698 
699   iface = G_APP_INFO_GET_IFACE (appinfo);
700   if (iface->launch_uris_async == NULL)
701     {
702       GTask *task;
703 
704       task = g_task_new (appinfo, cancellable, callback, user_data);
705       g_task_set_source_tag (task, g_app_info_launch_uris_async);
706       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
707                                "Operation not supported for the current backend.");
708       g_object_unref (task);
709 
710       return;
711     }
712 
713   (* iface->launch_uris_async) (appinfo, uris, context, cancellable, callback, user_data);
714 }
715 
716 /**
717  * g_app_info_launch_uris_finish:
718  * @appinfo: a #GAppInfo
719  * @result: a #GAsyncResult
720  * @error: (nullable): a #GError
721  *
722  * Finishes a g_app_info_launch_uris_async() operation.
723  *
724  * Returns: %TRUE on successful launch, %FALSE otherwise.
725  *
726  * Since: 2.60
727  */
728 gboolean
g_app_info_launch_uris_finish(GAppInfo * appinfo,GAsyncResult * result,GError ** error)729 g_app_info_launch_uris_finish (GAppInfo     *appinfo,
730                                GAsyncResult *result,
731                                GError      **error)
732 {
733   GAppInfoIface *iface;
734 
735   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
736 
737   iface = G_APP_INFO_GET_IFACE (appinfo);
738   if (iface->launch_uris_finish == NULL)
739     {
740       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
741                            "Operation not supported for the current backend.");
742       return FALSE;
743     }
744 
745   return (* iface->launch_uris_finish) (appinfo, result, error);
746 }
747 
748 /**
749  * g_app_info_should_show:
750  * @appinfo: a #GAppInfo.
751  *
752  * Checks if the application info should be shown in menus that
753  * list available applications.
754  *
755  * Returns: %TRUE if the @appinfo should be shown, %FALSE otherwise.
756  **/
757 gboolean
g_app_info_should_show(GAppInfo * appinfo)758 g_app_info_should_show (GAppInfo *appinfo)
759 {
760   GAppInfoIface *iface;
761 
762   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
763 
764   iface = G_APP_INFO_GET_IFACE (appinfo);
765 
766   return (* iface->should_show) (appinfo);
767 }
768 
769 /**
770  * g_app_info_launch_default_for_uri:
771  * @uri: the uri to show
772  * @context: (nullable): an optional #GAppLaunchContext
773  * @error: (nullable): return location for an error, or %NULL
774  *
775  * Utility function that launches the default application
776  * registered to handle the specified uri. Synchronous I/O
777  * is done on the uri to detect the type of the file if
778  * required.
779  *
780  * The D-Bus–activated applications don't have to be started if your application
781  * terminates too soon after this function. To prevent this, use
782  * g_app_info_launch_default_for_uri_async() instead.
783  *
784  * Returns: %TRUE on success, %FALSE on error.
785  **/
786 gboolean
g_app_info_launch_default_for_uri(const char * uri,GAppLaunchContext * launch_context,GError ** error)787 g_app_info_launch_default_for_uri (const char         *uri,
788                                    GAppLaunchContext  *launch_context,
789                                    GError            **error)
790 {
791   char *uri_scheme;
792   GAppInfo *app_info = NULL;
793   gboolean res = FALSE;
794 
795   /* g_file_query_default_handler() calls
796    * g_app_info_get_default_for_uri_scheme() too, but we have to do it
797    * here anyway in case GFile can't parse @uri correctly.
798    */
799   uri_scheme = g_uri_parse_scheme (uri);
800   if (uri_scheme && uri_scheme[0] != '\0')
801     app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
802   g_free (uri_scheme);
803 
804   if (!app_info)
805     {
806       GFile *file;
807 
808       file = g_file_new_for_uri (uri);
809       app_info = g_file_query_default_handler (file, NULL, error);
810       g_object_unref (file);
811     }
812 
813   if (app_info)
814     {
815       GList l;
816 
817       l.data = (char *)uri;
818       l.next = l.prev = NULL;
819       res = g_app_info_launch_uris (app_info, &l, launch_context, error);
820       g_object_unref (app_info);
821     }
822 
823 #ifdef G_OS_UNIX
824   if (!res && glib_should_use_portal ())
825     {
826       const char *parent_window = NULL;
827 
828       /* Reset any error previously set by launch_default_for_uri */
829       g_clear_error (error);
830 
831       if (launch_context && launch_context->priv->envp)
832         parent_window = g_environ_getenv (launch_context->priv->envp, "PARENT_WINDOW_ID");
833 
834       return g_openuri_portal_open_uri (uri, parent_window, error);
835     }
836 #endif
837 
838   return res;
839 }
840 
841 typedef struct
842 {
843   gchar *uri;
844   GAppLaunchContext *context;
845 } LaunchDefaultForUriData;
846 
847 static void
launch_default_for_uri_data_free(LaunchDefaultForUriData * data)848 launch_default_for_uri_data_free (LaunchDefaultForUriData *data)
849 {
850   g_free (data->uri);
851   g_clear_object (&data->context);
852   g_free (data);
853 }
854 
855 #ifdef G_OS_UNIX
856 static void
launch_default_for_uri_portal_open_uri_cb(GObject * object,GAsyncResult * result,gpointer user_data)857 launch_default_for_uri_portal_open_uri_cb (GObject      *object,
858                                            GAsyncResult *result,
859                                            gpointer      user_data)
860 {
861   GTask *task = G_TASK (user_data);
862   GError *error = NULL;
863 
864   if (g_openuri_portal_open_uri_finish (result, &error))
865     g_task_return_boolean (task, TRUE);
866   else
867     g_task_return_error (task, g_steal_pointer (&error));
868   g_object_unref (task);
869 }
870 #endif
871 
872 static void
launch_default_for_uri_portal_open_uri(GTask * task,GError * error)873 launch_default_for_uri_portal_open_uri (GTask *task, GError *error)
874 {
875 #ifdef G_OS_UNIX
876   LaunchDefaultForUriData *data = g_task_get_task_data (task);
877   GCancellable *cancellable = g_task_get_cancellable (task);
878 
879   if (glib_should_use_portal ())
880     {
881       const char *parent_window = NULL;
882 
883       /* Reset any error previously set by launch_default_for_uri */
884       g_error_free (error);
885 
886       if (data->context && data->context->priv->envp)
887         parent_window = g_environ_getenv (data->context->priv->envp,
888                                           "PARENT_WINDOW_ID");
889 
890       g_openuri_portal_open_uri_async (data->uri,
891                                        parent_window,
892                                        cancellable,
893                                        launch_default_for_uri_portal_open_uri_cb,
894                                        g_steal_pointer (&task));
895       return;
896     }
897 #endif
898 
899   g_task_return_error (task, g_steal_pointer (&error));
900   g_object_unref (task);
901 }
902 
903 static void
launch_default_for_uri_launch_uris_cb(GObject * object,GAsyncResult * result,gpointer user_data)904 launch_default_for_uri_launch_uris_cb (GObject      *object,
905                                        GAsyncResult *result,
906                                        gpointer      user_data)
907 {
908   GAppInfo *app_info = G_APP_INFO (object);
909   GTask *task = G_TASK (user_data);
910   GError *error = NULL;
911 
912   if (g_app_info_launch_uris_finish (app_info, result, &error))
913     {
914       g_task_return_boolean (task, TRUE);
915       g_object_unref (task);
916     }
917   else
918     launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
919 }
920 
921 static void
launch_default_for_uri_launch_uris(GTask * task,GAppInfo * app_info)922 launch_default_for_uri_launch_uris (GTask *task,
923                                     GAppInfo *app_info)
924 {
925   GCancellable *cancellable = g_task_get_cancellable (task);
926   GList l;
927   LaunchDefaultForUriData *data = g_task_get_task_data (task);
928 
929   l.data = (char *)data->uri;
930   l.next = l.prev = NULL;
931   g_app_info_launch_uris_async (app_info,
932                                 &l,
933                                 data->context,
934                                 cancellable,
935                                 launch_default_for_uri_launch_uris_cb,
936                                 g_steal_pointer (&task));
937   g_object_unref (app_info);
938 }
939 
940 static void
launch_default_for_uri_default_handler_cb(GObject * object,GAsyncResult * result,gpointer user_data)941 launch_default_for_uri_default_handler_cb (GObject      *object,
942                                            GAsyncResult *result,
943                                            gpointer      user_data)
944 {
945   GFile *file = G_FILE (object);
946   GTask *task = G_TASK (user_data);
947   GAppInfo *app_info = NULL;
948   GError *error = NULL;
949 
950   app_info = g_file_query_default_handler_finish (file, result, &error);
951   if (app_info)
952     launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
953   else
954     launch_default_for_uri_portal_open_uri (g_steal_pointer (&task), g_steal_pointer (&error));
955 }
956 
957 /**
958  * g_app_info_launch_default_for_uri_async:
959  * @uri: the uri to show
960  * @context: (nullable): an optional #GAppLaunchContext
961  * @cancellable: (nullable): a #GCancellable
962  * @callback: (nullable): a #GAsyncReadyCallback to call when the request is done
963  * @user_data: (nullable): data to pass to @callback
964  *
965  * Async version of g_app_info_launch_default_for_uri().
966  *
967  * This version is useful if you are interested in receiving
968  * error information in the case where the application is
969  * sandboxed and the portal may present an application chooser
970  * dialog to the user.
971  *
972  * This is also useful if you want to be sure that the D-Bus–activated
973  * applications are really started before termination and if you are interested
974  * in receiving error information from their activation.
975  *
976  * Since: 2.50
977  */
978 void
g_app_info_launch_default_for_uri_async(const char * uri,GAppLaunchContext * context,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)979 g_app_info_launch_default_for_uri_async (const char          *uri,
980                                          GAppLaunchContext   *context,
981                                          GCancellable        *cancellable,
982                                          GAsyncReadyCallback  callback,
983                                          gpointer             user_data)
984 {
985   GTask *task;
986   char *uri_scheme;
987   GAppInfo *app_info = NULL;
988   LaunchDefaultForUriData *data;
989 
990   g_return_if_fail (uri != NULL);
991 
992   task = g_task_new (NULL, cancellable, callback, user_data);
993   g_task_set_source_tag (task, g_app_info_launch_default_for_uri_async);
994 
995   data = g_new (LaunchDefaultForUriData, 1);
996   data->uri = g_strdup (uri);
997   data->context = (context != NULL) ? g_object_ref (context) : NULL;
998   g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) launch_default_for_uri_data_free);
999 
1000   /* g_file_query_default_handler_async() calls
1001    * g_app_info_get_default_for_uri_scheme() too, but we have to do it
1002    * here anyway in case GFile can't parse @uri correctly.
1003    */
1004   uri_scheme = g_uri_parse_scheme (uri);
1005   if (uri_scheme && uri_scheme[0] != '\0')
1006     /* FIXME: The following still uses blocking calls. */
1007     app_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
1008   g_free (uri_scheme);
1009 
1010   if (!app_info)
1011     {
1012       GFile *file;
1013 
1014       file = g_file_new_for_uri (uri);
1015       g_file_query_default_handler_async (file,
1016                                           G_PRIORITY_DEFAULT,
1017                                           cancellable,
1018                                           launch_default_for_uri_default_handler_cb,
1019                                           g_steal_pointer (&task));
1020       g_object_unref (file);
1021     }
1022   else
1023     launch_default_for_uri_launch_uris (g_steal_pointer (&task), g_steal_pointer (&app_info));
1024 }
1025 
1026 /**
1027  * g_app_info_launch_default_for_uri_finish:
1028  * @result: a #GAsyncResult
1029  * @error: (nullable): return location for an error, or %NULL
1030  *
1031  * Finishes an asynchronous launch-default-for-uri operation.
1032  *
1033  * Returns: %TRUE if the launch was successful, %FALSE if @error is set
1034  *
1035  * Since: 2.50
1036  */
1037 gboolean
g_app_info_launch_default_for_uri_finish(GAsyncResult * result,GError ** error)1038 g_app_info_launch_default_for_uri_finish (GAsyncResult  *result,
1039                                           GError       **error)
1040 {
1041   g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
1042 
1043   return g_task_propagate_boolean (G_TASK (result), error);
1044 }
1045 
1046 /**
1047  * g_app_info_can_delete:
1048  * @appinfo: a #GAppInfo
1049  *
1050  * Obtains the information whether the #GAppInfo can be deleted.
1051  * See g_app_info_delete().
1052  *
1053  * Returns: %TRUE if @appinfo can be deleted
1054  *
1055  * Since: 2.20
1056  */
1057 gboolean
g_app_info_can_delete(GAppInfo * appinfo)1058 g_app_info_can_delete (GAppInfo *appinfo)
1059 {
1060   GAppInfoIface *iface;
1061 
1062   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
1063 
1064   iface = G_APP_INFO_GET_IFACE (appinfo);
1065 
1066   if (iface->can_delete)
1067     return (* iface->can_delete) (appinfo);
1068 
1069   return FALSE;
1070 }
1071 
1072 
1073 /**
1074  * g_app_info_delete:
1075  * @appinfo: a #GAppInfo
1076  *
1077  * Tries to delete a #GAppInfo.
1078  *
1079  * On some platforms, there may be a difference between user-defined
1080  * #GAppInfos which can be deleted, and system-wide ones which cannot.
1081  * See g_app_info_can_delete().
1082  *
1083  * Virtual: do_delete
1084  * Returns: %TRUE if @appinfo has been deleted
1085  *
1086  * Since: 2.20
1087  */
1088 gboolean
g_app_info_delete(GAppInfo * appinfo)1089 g_app_info_delete (GAppInfo *appinfo)
1090 {
1091   GAppInfoIface *iface;
1092 
1093   g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
1094 
1095   iface = G_APP_INFO_GET_IFACE (appinfo);
1096 
1097   if (iface->do_delete)
1098     return (* iface->do_delete) (appinfo);
1099 
1100   return FALSE;
1101 }
1102 
1103 
1104 enum {
1105   LAUNCH_FAILED,
1106   LAUNCHED,
1107   LAST_SIGNAL
1108 };
1109 
1110 static guint signals[LAST_SIGNAL] = { 0 };
1111 
G_DEFINE_TYPE_WITH_PRIVATE(GAppLaunchContext,g_app_launch_context,G_TYPE_OBJECT)1112 G_DEFINE_TYPE_WITH_PRIVATE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT)
1113 
1114 /**
1115  * g_app_launch_context_new:
1116  *
1117  * Creates a new application launch context. This is not normally used,
1118  * instead you instantiate a subclass of this, such as #GdkAppLaunchContext.
1119  *
1120  * Returns: a #GAppLaunchContext.
1121  **/
1122 GAppLaunchContext *
1123 g_app_launch_context_new (void)
1124 {
1125   return g_object_new (G_TYPE_APP_LAUNCH_CONTEXT, NULL);
1126 }
1127 
1128 static void
g_app_launch_context_finalize(GObject * object)1129 g_app_launch_context_finalize (GObject *object)
1130 {
1131   GAppLaunchContext *context = G_APP_LAUNCH_CONTEXT (object);
1132 
1133   g_strfreev (context->priv->envp);
1134 
1135   G_OBJECT_CLASS (g_app_launch_context_parent_class)->finalize (object);
1136 }
1137 
1138 static void
g_app_launch_context_class_init(GAppLaunchContextClass * klass)1139 g_app_launch_context_class_init (GAppLaunchContextClass *klass)
1140 {
1141   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1142 
1143   object_class->finalize = g_app_launch_context_finalize;
1144 
1145   /**
1146    * GAppLaunchContext::launch-failed:
1147    * @context: the object emitting the signal
1148    * @startup_notify_id: the startup notification id for the failed launch
1149    *
1150    * The ::launch-failed signal is emitted when a #GAppInfo launch
1151    * fails. The startup notification id is provided, so that the launcher
1152    * can cancel the startup notification.
1153    *
1154    * Since: 2.36
1155    */
1156   signals[LAUNCH_FAILED] = g_signal_new (I_("launch-failed"),
1157                                          G_OBJECT_CLASS_TYPE (object_class),
1158                                          G_SIGNAL_RUN_LAST,
1159                                          G_STRUCT_OFFSET (GAppLaunchContextClass, launch_failed),
1160                                          NULL, NULL, NULL,
1161                                          G_TYPE_NONE, 1, G_TYPE_STRING);
1162 
1163   /**
1164    * GAppLaunchContext::launched:
1165    * @context: the object emitting the signal
1166    * @info: the #GAppInfo that was just launched
1167    * @platform_data: additional platform-specific data for this launch
1168    *
1169    * The ::launched signal is emitted when a #GAppInfo is successfully
1170    * launched. The @platform_data is an GVariant dictionary mapping
1171    * strings to variants (ie a{sv}), which contains additional,
1172    * platform-specific data about this launch. On UNIX, at least the
1173    * "pid" and "startup-notification-id" keys will be present.
1174    *
1175    * Since: 2.36
1176    */
1177   signals[LAUNCHED] = g_signal_new (I_("launched"),
1178                                     G_OBJECT_CLASS_TYPE (object_class),
1179                                     G_SIGNAL_RUN_LAST,
1180                                     G_STRUCT_OFFSET (GAppLaunchContextClass, launched),
1181                                     NULL, NULL,
1182                                     _g_cclosure_marshal_VOID__OBJECT_VARIANT,
1183                                     G_TYPE_NONE, 2,
1184                                     G_TYPE_APP_INFO, G_TYPE_VARIANT);
1185   g_signal_set_va_marshaller (signals[LAUNCHED],
1186                               G_TYPE_FROM_CLASS (klass),
1187                               _g_cclosure_marshal_VOID__OBJECT_VARIANTv);
1188 }
1189 
1190 static void
g_app_launch_context_init(GAppLaunchContext * context)1191 g_app_launch_context_init (GAppLaunchContext *context)
1192 {
1193   context->priv = g_app_launch_context_get_instance_private (context);
1194 }
1195 
1196 /**
1197  * g_app_launch_context_setenv:
1198  * @context: a #GAppLaunchContext
1199  * @variable: (type filename): the environment variable to set
1200  * @value: (type filename): the value for to set the variable to.
1201  *
1202  * Arranges for @variable to be set to @value in the child's
1203  * environment when @context is used to launch an application.
1204  *
1205  * Since: 2.32
1206  */
1207 void
g_app_launch_context_setenv(GAppLaunchContext * context,const char * variable,const char * value)1208 g_app_launch_context_setenv (GAppLaunchContext *context,
1209                              const char        *variable,
1210                              const char        *value)
1211 {
1212   g_return_if_fail (G_IS_APP_LAUNCH_CONTEXT (context));
1213   g_return_if_fail (variable != NULL);
1214   g_return_if_fail (value != NULL);
1215 
1216   if (!context->priv->envp)
1217     context->priv->envp = g_get_environ ();
1218 
1219   context->priv->envp =
1220     g_environ_setenv (context->priv->envp, variable, value, TRUE);
1221 }
1222 
1223 /**
1224  * g_app_launch_context_unsetenv:
1225  * @context: a #GAppLaunchContext
1226  * @variable: (type filename): the environment variable to remove
1227  *
1228  * Arranges for @variable to be unset in the child's environment
1229  * when @context is used to launch an application.
1230  *
1231  * Since: 2.32
1232  */
1233 void
g_app_launch_context_unsetenv(GAppLaunchContext * context,const char * variable)1234 g_app_launch_context_unsetenv (GAppLaunchContext *context,
1235                                const char        *variable)
1236 {
1237   g_return_if_fail (G_IS_APP_LAUNCH_CONTEXT (context));
1238   g_return_if_fail (variable != NULL);
1239 
1240   if (!context->priv->envp)
1241     context->priv->envp = g_get_environ ();
1242 
1243   context->priv->envp =
1244     g_environ_unsetenv (context->priv->envp, variable);
1245 }
1246 
1247 /**
1248  * g_app_launch_context_get_environment:
1249  * @context: a #GAppLaunchContext
1250  *
1251  * Gets the complete environment variable list to be passed to
1252  * the child process when @context is used to launch an application.
1253  * This is a %NULL-terminated array of strings, where each string has
1254  * the form `KEY=VALUE`.
1255  *
1256  * Returns: (array zero-terminated=1) (element-type filename) (transfer full):
1257  *     the child's environment
1258  *
1259  * Since: 2.32
1260  */
1261 char **
g_app_launch_context_get_environment(GAppLaunchContext * context)1262 g_app_launch_context_get_environment (GAppLaunchContext *context)
1263 {
1264   g_return_val_if_fail (G_IS_APP_LAUNCH_CONTEXT (context), NULL);
1265 
1266   if (!context->priv->envp)
1267     context->priv->envp = g_get_environ ();
1268 
1269   return g_strdupv (context->priv->envp);
1270 }
1271 
1272 /**
1273  * g_app_launch_context_get_display:
1274  * @context: a #GAppLaunchContext
1275  * @info: a #GAppInfo
1276  * @files: (element-type GFile): a #GList of #GFile objects
1277  *
1278  * Gets the display string for the @context. This is used to ensure new
1279  * applications are started on the same display as the launching
1280  * application, by setting the `DISPLAY` environment variable.
1281  *
1282  * Returns: (nullable): a display string for the display.
1283  */
1284 char *
g_app_launch_context_get_display(GAppLaunchContext * context,GAppInfo * info,GList * files)1285 g_app_launch_context_get_display (GAppLaunchContext *context,
1286 				  GAppInfo          *info,
1287 				  GList             *files)
1288 {
1289   GAppLaunchContextClass *class;
1290 
1291   g_return_val_if_fail (G_IS_APP_LAUNCH_CONTEXT (context), NULL);
1292   g_return_val_if_fail (G_IS_APP_INFO (info), NULL);
1293 
1294   class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context);
1295 
1296   if (class->get_display == NULL)
1297     return NULL;
1298 
1299   return class->get_display (context, info, files);
1300 }
1301 
1302 /**
1303  * g_app_launch_context_get_startup_notify_id:
1304  * @context: a #GAppLaunchContext
1305  * @info: a #GAppInfo
1306  * @files: (element-type GFile): a #GList of of #GFile objects
1307  *
1308  * Initiates startup notification for the application and returns the
1309  * `DESKTOP_STARTUP_ID` for the launched operation, if supported.
1310  *
1311  * Startup notification IDs are defined in the
1312  * [FreeDesktop.Org Startup Notifications standard](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt).
1313  *
1314  * Returns: (nullable): a startup notification ID for the application, or %NULL if
1315  *     not supported.
1316  **/
1317 char *
g_app_launch_context_get_startup_notify_id(GAppLaunchContext * context,GAppInfo * info,GList * files)1318 g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
1319 					    GAppInfo          *info,
1320 					    GList             *files)
1321 {
1322   GAppLaunchContextClass *class;
1323 
1324   g_return_val_if_fail (G_IS_APP_LAUNCH_CONTEXT (context), NULL);
1325   g_return_val_if_fail (G_IS_APP_INFO (info), NULL);
1326 
1327   class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context);
1328 
1329   if (class->get_startup_notify_id == NULL)
1330     return NULL;
1331 
1332   return class->get_startup_notify_id (context, info, files);
1333 }
1334 
1335 
1336 /**
1337  * g_app_launch_context_launch_failed:
1338  * @context: a #GAppLaunchContext.
1339  * @startup_notify_id: the startup notification id that was returned by g_app_launch_context_get_startup_notify_id().
1340  *
1341  * Called when an application has failed to launch, so that it can cancel
1342  * the application startup notification started in g_app_launch_context_get_startup_notify_id().
1343  *
1344  **/
1345 void
g_app_launch_context_launch_failed(GAppLaunchContext * context,const char * startup_notify_id)1346 g_app_launch_context_launch_failed (GAppLaunchContext *context,
1347 				    const char        *startup_notify_id)
1348 {
1349   g_return_if_fail (G_IS_APP_LAUNCH_CONTEXT (context));
1350   g_return_if_fail (startup_notify_id != NULL);
1351 
1352   g_signal_emit (context, signals[LAUNCH_FAILED], 0, startup_notify_id);
1353 }
1354 
1355 
1356 /**
1357  * SECTION:gappinfomonitor
1358  * @short_description: Monitor application information for changes
1359  *
1360  * #GAppInfoMonitor is a very simple object used for monitoring the app
1361  * info database for changes (ie: newly installed or removed
1362  * applications).
1363  *
1364  * Call g_app_info_monitor_get() to get a #GAppInfoMonitor and connect
1365  * to the "changed" signal.
1366  *
1367  * In the usual case, applications should try to make note of the change
1368  * (doing things like invalidating caches) but not act on it.  In
1369  * particular, applications should avoid making calls to #GAppInfo APIs
1370  * in response to the change signal, deferring these until the time that
1371  * the data is actually required.  The exception to this case is when
1372  * application information is actually being displayed on the screen
1373  * (eg: during a search or when the list of all applications is shown).
1374  * The reason for this is that changes to the list of installed
1375  * applications often come in groups (like during system updates) and
1376  * rescanning the list on every change is pointless and expensive.
1377  *
1378  * Since: 2.40
1379  **/
1380 
1381 /**
1382  * GAppInfoMonitor:
1383  *
1384  * The only thing you can do with this is to get it via
1385  * g_app_info_monitor_get() and connect to the "changed" signal.
1386  *
1387  * Since: 2.40
1388  **/
1389 
1390 typedef struct _GAppInfoMonitorClass GAppInfoMonitorClass;
1391 
1392 struct _GAppInfoMonitor
1393 {
1394   GObject parent_instance;
1395   GMainContext *context;
1396 };
1397 
1398 struct _GAppInfoMonitorClass
1399 {
1400   GObjectClass parent_class;
1401 };
1402 
1403 static GContextSpecificGroup g_app_info_monitor_group;
1404 static guint                 g_app_info_monitor_changed_signal;
1405 
G_DEFINE_TYPE(GAppInfoMonitor,g_app_info_monitor,G_TYPE_OBJECT)1406 G_DEFINE_TYPE (GAppInfoMonitor, g_app_info_monitor, G_TYPE_OBJECT)
1407 
1408 static void
1409 g_app_info_monitor_finalize (GObject *object)
1410 {
1411   GAppInfoMonitor *monitor = G_APP_INFO_MONITOR (object);
1412 
1413   g_context_specific_group_remove (&g_app_info_monitor_group, monitor->context, monitor, NULL);
1414 
1415   G_OBJECT_CLASS (g_app_info_monitor_parent_class)->finalize (object);
1416 }
1417 
1418 static void
g_app_info_monitor_init(GAppInfoMonitor * monitor)1419 g_app_info_monitor_init (GAppInfoMonitor *monitor)
1420 {
1421 }
1422 
1423 static void
g_app_info_monitor_class_init(GAppInfoMonitorClass * class)1424 g_app_info_monitor_class_init (GAppInfoMonitorClass *class)
1425 {
1426   GObjectClass *object_class = G_OBJECT_CLASS (class);
1427 
1428   /**
1429    * GAppInfoMonitor::changed:
1430    *
1431    * Signal emitted when the app info database for changes (ie: newly installed
1432    * or removed applications).
1433    **/
1434   g_app_info_monitor_changed_signal = g_signal_new (I_("changed"), G_TYPE_APP_INFO_MONITOR, G_SIGNAL_RUN_FIRST,
1435                                                     0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1436 
1437   object_class->finalize = g_app_info_monitor_finalize;
1438 }
1439 
1440 /**
1441  * g_app_info_monitor_get:
1442  *
1443  * Gets the #GAppInfoMonitor for the current thread-default main
1444  * context.
1445  *
1446  * The #GAppInfoMonitor will emit a "changed" signal in the
1447  * thread-default main context whenever the list of installed
1448  * applications (as reported by g_app_info_get_all()) may have changed.
1449  *
1450  * You must only call g_object_unref() on the return value from under
1451  * the same main context as you created it.
1452  *
1453  * Returns: (transfer full): a reference to a #GAppInfoMonitor
1454  *
1455  * Since: 2.40
1456  **/
1457 GAppInfoMonitor *
g_app_info_monitor_get(void)1458 g_app_info_monitor_get (void)
1459 {
1460   return g_context_specific_group_get (&g_app_info_monitor_group,
1461                                        G_TYPE_APP_INFO_MONITOR,
1462                                        G_STRUCT_OFFSET (GAppInfoMonitor, context),
1463                                        NULL);
1464 }
1465 
1466 void
g_app_info_monitor_fire(void)1467 g_app_info_monitor_fire (void)
1468 {
1469   g_context_specific_group_emit (&g_app_info_monitor_group, g_app_info_monitor_changed_signal);
1470 }
1471