Coverage Report

Created: 2025-06-13 06:55

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