1 /* eggdesktopfile.c - Freedesktop.Org Desktop Files
2 * Copyright (C) 2007 Novell, Inc.
4 * Based on gnome-desktop-item.c
5 * Copyright (C) 1999, 2000 Red Hat Inc.
6 * Copyright (C) 2001 George Lebl
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; see the file COPYING.LIB. If not,
20 * write to the Free Software Foundation, Inc., 59 Temple Place -
21 * Suite 330, Boston, MA 02111-1307, USA.
28 #include "egg-desktop-file.h"
33 #include <glib/gi18n.h>
37 struct EggDesktopFile
{
42 EggDesktopFileType type
;
47 * egg_desktop_file_new:
48 * @desktop_file_path: path to a Freedesktop-style Desktop file
49 * @error: error pointer
51 * Creates a new #EggDesktopFile for @desktop_file.
53 * Return value: the new #EggDesktopFile, or %NULL on error.
56 egg_desktop_file_new (const char *desktop_file_path
, GError
**error
)
60 key_file
= g_key_file_new ();
61 if (!g_key_file_load_from_file (key_file
, desktop_file_path
, 0, error
))
63 g_key_file_free (key_file
);
67 return egg_desktop_file_new_from_key_file (key_file
, desktop_file_path
,
72 * egg_desktop_file_new_from_data_dirs:
73 * @desktop_file_path: relative path to a Freedesktop-style Desktop file
74 * @error: error pointer
76 * Looks for @desktop_file_path in the paths returned from
77 * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
78 * a new #EggDesktopFile from it.
80 * Return value: the new #EggDesktopFile, or %NULL on error.
83 egg_desktop_file_new_from_data_dirs (const char *desktop_file_path
,
86 EggDesktopFile
*desktop_file
;
90 key_file
= g_key_file_new ();
91 if (!g_key_file_load_from_data_dirs (key_file
, desktop_file_path
,
92 &full_path
, 0, error
))
94 g_key_file_free (key_file
);
98 desktop_file
= egg_desktop_file_new_from_key_file (key_file
,
106 * egg_desktop_file_new_from_dirs:
107 * @desktop_file_path: relative path to a Freedesktop-style Desktop file
108 * @search_dirs: NULL-terminated array of directories to search
109 * @error: error pointer
111 * Looks for @desktop_file_path in the paths returned from
112 * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
113 * a new #EggDesktopFile from it.
115 * Return value: the new #EggDesktopFile, or %NULL on error.
118 egg_desktop_file_new_from_dirs (const char *desktop_file_path
,
119 const char **search_dirs
,
122 EggDesktopFile
*desktop_file
;
126 key_file
= g_key_file_new ();
127 if (!g_key_file_load_from_dirs (key_file
, desktop_file_path
, search_dirs
,
128 &full_path
, 0, error
))
130 g_key_file_free (key_file
);
134 desktop_file
= egg_desktop_file_new_from_key_file (key_file
,
142 * egg_desktop_file_new_from_key_file:
143 * @key_file: a #GKeyFile representing a desktop file
144 * @source: the path or URI that @key_file was loaded from, or %NULL
145 * @error: error pointer
147 * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
148 * @key_file (on success or failure); you should consider @key_file to
149 * be freed after calling this function.
151 * Return value: the new #EggDesktopFile, or %NULL on error.
154 egg_desktop_file_new_from_key_file (GKeyFile
*key_file
,
158 EggDesktopFile
*desktop_file
;
159 char *version
, *type
;
161 if (!g_key_file_has_group (key_file
, EGG_DESKTOP_FILE_GROUP
))
163 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
164 EGG_DESKTOP_FILE_ERROR_INVALID
,
165 _("File is not a valid .desktop file"));
166 g_key_file_free (key_file
);
170 version
= g_key_file_get_value (key_file
, EGG_DESKTOP_FILE_GROUP
,
171 EGG_DESKTOP_FILE_KEY_VERSION
,
178 version_num
= g_ascii_strtod (version
, &end
);
181 g_warning ("Invalid Version string '%s' in %s",
182 version
, source
? source
: "(unknown)");
184 else if (version_num
> 1.0)
186 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
187 EGG_DESKTOP_FILE_ERROR_INVALID
,
188 _("Unrecognized desktop file Version '%s'"), version
);
190 g_key_file_free (key_file
);
196 desktop_file
= g_new0 (EggDesktopFile
, 1);
197 desktop_file
->key_file
= key_file
;
199 if (g_path_is_absolute (source
))
200 desktop_file
->source
= g_filename_to_uri (source
, NULL
, NULL
);
202 desktop_file
->source
= g_strdup (source
);
204 desktop_file
->name
= g_key_file_get_string (key_file
, EGG_DESKTOP_FILE_GROUP
,
205 EGG_DESKTOP_FILE_KEY_NAME
, error
);
206 if (!desktop_file
->name
)
208 egg_desktop_file_free (desktop_file
);
212 type
= g_key_file_get_string (key_file
, EGG_DESKTOP_FILE_GROUP
,
213 EGG_DESKTOP_FILE_KEY_TYPE
, error
);
216 egg_desktop_file_free (desktop_file
);
220 if (!strcmp (type
, "Application"))
224 desktop_file
->type
= EGG_DESKTOP_FILE_TYPE_APPLICATION
;
226 exec
= g_key_file_get_string (key_file
,
227 EGG_DESKTOP_FILE_GROUP
,
228 EGG_DESKTOP_FILE_KEY_EXEC
,
232 egg_desktop_file_free (desktop_file
);
237 /* See if it takes paths or URIs or neither */
238 for (p
= exec
; *p
; p
++)
242 if (p
[1] == '\0' || strchr ("FfUu", p
[1]))
244 desktop_file
->document_code
= p
[1];
253 else if (!strcmp (type
, "Link"))
257 desktop_file
->type
= EGG_DESKTOP_FILE_TYPE_LINK
;
259 url
= g_key_file_get_string (key_file
,
260 EGG_DESKTOP_FILE_GROUP
,
261 EGG_DESKTOP_FILE_KEY_URL
,
265 egg_desktop_file_free (desktop_file
);
271 else if (!strcmp (type
, "Directory"))
272 desktop_file
->type
= EGG_DESKTOP_FILE_TYPE_DIRECTORY
;
274 desktop_file
->type
= EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED
;
278 /* Check the Icon key */
279 desktop_file
->icon
= g_key_file_get_string (key_file
,
280 EGG_DESKTOP_FILE_GROUP
,
281 EGG_DESKTOP_FILE_KEY_ICON
,
283 if (desktop_file
->icon
&& !g_path_is_absolute (desktop_file
->icon
))
287 /* Lots of .desktop files still get this wrong */
288 ext
= strrchr (desktop_file
->icon
, '.');
289 if (ext
&& (!strcmp (ext
, ".png") ||
290 !strcmp (ext
, ".xpm") ||
291 !strcmp (ext
, ".svg")))
293 g_warning ("Desktop file '%s' has malformed Icon key '%s'"
294 "(should not include extension)",
295 source
? source
: "(unknown)",
305 * egg_desktop_file_free:
306 * @desktop_file: an #EggDesktopFile
308 * Frees @desktop_file.
311 egg_desktop_file_free (EggDesktopFile
*desktop_file
)
313 g_key_file_free (desktop_file
->key_file
);
314 g_free (desktop_file
->source
);
315 g_free (desktop_file
->name
);
316 g_free (desktop_file
->icon
);
317 g_free (desktop_file
);
321 * egg_desktop_file_get_source:
322 * @desktop_file: an #EggDesktopFile
324 * Gets the URI that @desktop_file was loaded from.
326 * Return value: @desktop_file's source URI
329 egg_desktop_file_get_source (EggDesktopFile
*desktop_file
)
331 return desktop_file
->source
;
335 * egg_desktop_file_get_desktop_file_type:
336 * @desktop_file: an #EggDesktopFile
338 * Gets the desktop file type of @desktop_file.
340 * Return value: @desktop_file's type
343 egg_desktop_file_get_desktop_file_type (EggDesktopFile
*desktop_file
)
345 return desktop_file
->type
;
349 * egg_desktop_file_get_name:
350 * @desktop_file: an #EggDesktopFile
352 * Gets the (localized) value of @desktop_file's "Name" key.
354 * Return value: the application/link name
357 egg_desktop_file_get_name (EggDesktopFile
*desktop_file
)
359 return desktop_file
->name
;
363 * egg_desktop_file_get_icon:
364 * @desktop_file: an #EggDesktopFile
366 * Gets the value of @desktop_file's "Icon" key.
368 * If the icon string is a full path (that is, if g_path_is_absolute()
369 * returns %TRUE when called on it), it points to a file containing an
370 * unthemed icon. If the icon string is not a full path, it is the
371 * name of a themed icon, which can be looked up with %GtkIconTheme,
372 * or passed directly to a theme-aware widget like %GtkImage or
373 * %GtkCellRendererPixbuf.
375 * Return value: the icon path or name
378 egg_desktop_file_get_icon (EggDesktopFile
*desktop_file
)
380 return desktop_file
->icon
;
384 egg_desktop_file_has_key (EggDesktopFile
*desktop_file
,
388 return g_key_file_has_key (desktop_file
->key_file
,
389 EGG_DESKTOP_FILE_GROUP
, key
,
394 egg_desktop_file_get_string (EggDesktopFile
*desktop_file
,
398 return g_key_file_get_string (desktop_file
->key_file
,
399 EGG_DESKTOP_FILE_GROUP
, key
,
404 egg_desktop_file_get_locale_string (EggDesktopFile
*desktop_file
,
409 return g_key_file_get_locale_string (desktop_file
->key_file
,
410 EGG_DESKTOP_FILE_GROUP
, key
, locale
,
415 egg_desktop_file_get_boolean (EggDesktopFile
*desktop_file
,
419 return g_key_file_get_boolean (desktop_file
->key_file
,
420 EGG_DESKTOP_FILE_GROUP
, key
,
425 egg_desktop_file_get_numeric (EggDesktopFile
*desktop_file
,
429 return g_key_file_get_double (desktop_file
->key_file
,
430 EGG_DESKTOP_FILE_GROUP
, key
,
435 egg_desktop_file_get_string_list (EggDesktopFile
*desktop_file
,
440 return g_key_file_get_string_list (desktop_file
->key_file
,
441 EGG_DESKTOP_FILE_GROUP
, key
, length
,
446 egg_desktop_file_get_locale_string_list (EggDesktopFile
*desktop_file
,
452 return g_key_file_get_locale_string_list (desktop_file
->key_file
,
453 EGG_DESKTOP_FILE_GROUP
, key
,
459 * egg_desktop_file_can_launch:
460 * @desktop_file: an #EggDesktopFile
461 * @desktop_environment: the name of the running desktop environment,
464 * Tests if @desktop_file can/should be launched in the current
465 * environment. If @desktop_environment is non-%NULL, @desktop_file's
466 * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
467 * this desktop_file is appropriate for the named environment.
469 * Furthermore, if @desktop_file has type
470 * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
471 * also checked, to make sure the binary it points to exists.
473 * egg_desktop_file_can_launch() does NOT check the value of the
476 * Return value: %TRUE if @desktop_file can be launched
479 egg_desktop_file_can_launch (EggDesktopFile
*desktop_file
,
480 const char *desktop_environment
)
482 char *try_exec
, *found_program
;
483 char **only_show_in
, **not_show_in
;
487 if (desktop_file
->type
!= EGG_DESKTOP_FILE_TYPE_APPLICATION
&&
488 desktop_file
->type
!= EGG_DESKTOP_FILE_TYPE_LINK
)
491 if (desktop_environment
)
493 only_show_in
= g_key_file_get_string_list (desktop_file
->key_file
,
494 EGG_DESKTOP_FILE_GROUP
,
495 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN
,
499 for (i
= 0, found
= FALSE
; only_show_in
[i
] && !found
; i
++)
501 if (!strcmp (only_show_in
[i
], desktop_environment
))
505 g_strfreev (only_show_in
);
511 not_show_in
= g_key_file_get_string_list (desktop_file
->key_file
,
512 EGG_DESKTOP_FILE_GROUP
,
513 EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN
,
517 for (i
= 0, found
= FALSE
; not_show_in
[i
] && !found
; i
++)
519 if (!strcmp (not_show_in
[i
], desktop_environment
))
523 g_strfreev (not_show_in
);
530 if (desktop_file
->type
== EGG_DESKTOP_FILE_TYPE_APPLICATION
)
532 try_exec
= g_key_file_get_string (desktop_file
->key_file
,
533 EGG_DESKTOP_FILE_GROUP
,
534 EGG_DESKTOP_FILE_KEY_TRY_EXEC
,
538 found_program
= g_find_program_in_path (try_exec
);
543 g_free (found_program
);
551 * egg_desktop_file_accepts_documents:
552 * @desktop_file: an #EggDesktopFile
554 * Tests if @desktop_file represents an application that can accept
555 * documents on the command line.
557 * Return value: %TRUE or %FALSE
560 egg_desktop_file_accepts_documents (EggDesktopFile
*desktop_file
)
562 return desktop_file
->document_code
!= 0;
566 * egg_desktop_file_accepts_multiple:
567 * @desktop_file: an #EggDesktopFile
569 * Tests if @desktop_file can accept multiple documents at once.
571 * If this returns %FALSE, you can still pass multiple documents to
572 * egg_desktop_file_launch(), but that will result in multiple copies
573 * of the application being launched. See egg_desktop_file_launch()
576 * Return value: %TRUE or %FALSE
579 egg_desktop_file_accepts_multiple (EggDesktopFile
*desktop_file
)
581 return (desktop_file
->document_code
== 'F' ||
582 desktop_file
->document_code
== 'U');
586 * egg_desktop_file_accepts_uris:
587 * @desktop_file: an #EggDesktopFile
589 * Tests if @desktop_file can accept (non-"file:") URIs as documents to
592 * Return value: %TRUE or %FALSE
595 egg_desktop_file_accepts_uris (EggDesktopFile
*desktop_file
)
597 return (desktop_file
->document_code
== 'U' ||
598 desktop_file
->document_code
== 'u');
602 append_quoted_word (GString
*str
,
604 gboolean in_single_quotes
,
605 gboolean in_double_quotes
)
609 if (!in_single_quotes
&& !in_double_quotes
)
610 g_string_append_c (str
, '\'');
611 else if (!in_single_quotes
&& in_double_quotes
)
612 g_string_append (str
, "\"'");
614 if (!strchr (s
, '\''))
615 g_string_append (str
, s
);
618 for (p
= s
; *p
!= '\0'; p
++)
621 g_string_append (str
, "'\\''");
623 g_string_append_c (str
, *p
);
627 if (!in_single_quotes
&& !in_double_quotes
)
628 g_string_append_c (str
, '\'');
629 else if (!in_single_quotes
&& in_double_quotes
)
630 g_string_append (str
, "'\"");
634 do_percent_subst (EggDesktopFile
*desktop_file
,
638 gboolean in_single_quotes
,
639 gboolean in_double_quotes
)
647 g_string_append_c (str
, '%');
652 for (d
= *documents
; d
; d
= d
->next
)
655 g_string_append (str
, " ");
656 append_quoted_word (str
, doc
, in_single_quotes
, in_double_quotes
);
665 doc
= (*documents
)->data
;
666 g_string_append (str
, " ");
667 append_quoted_word (str
, doc
, in_single_quotes
, in_double_quotes
);
668 *documents
= (*documents
)->next
;
673 if (desktop_file
->icon
)
675 g_string_append (str
, "--icon ");
676 append_quoted_word (str
, desktop_file
->icon
,
677 in_single_quotes
, in_double_quotes
);
682 if (desktop_file
->name
)
684 append_quoted_word (str
, desktop_file
->name
,
685 in_single_quotes
, in_double_quotes
);
690 if (desktop_file
->source
)
692 append_quoted_word (str
, desktop_file
->source
,
693 in_single_quotes
, in_double_quotes
);
703 /* Deprecated; skip */
707 g_warning ("Unrecognized %%-code '%%%c' in Exec", code
);
713 parse_exec (EggDesktopFile
*desktop_file
,
717 char *exec
, *p
, *command
;
718 gboolean escape
, single_quot
, double_quot
;
721 exec
= g_key_file_get_string (desktop_file
->key_file
,
722 EGG_DESKTOP_FILE_GROUP
,
723 EGG_DESKTOP_FILE_KEY_EXEC
,
728 /* Build the command */
729 gs
= g_string_new (NULL
);
730 escape
= single_quot
= double_quot
= FALSE
;
732 for (p
= exec
; *p
!= '\0'; p
++)
737 g_string_append_c (gs
, *p
);
743 g_string_append_c (gs
, *p
);
747 g_string_append_c (gs
, *p
);
748 if (!single_quot
&& !double_quot
)
750 else if (single_quot
)
755 g_string_append_c (gs
, *p
);
756 if (!single_quot
&& !double_quot
)
758 else if (double_quot
)
761 else if (*p
== '%' && p
[1])
763 do_percent_subst (desktop_file
, p
[1], gs
, documents
,
764 single_quot
, double_quot
);
768 g_string_append_c (gs
, *p
);
772 command
= g_string_free (gs
, FALSE
);
774 /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
775 if (g_key_file_has_key (desktop_file
->key_file
,
776 EGG_DESKTOP_FILE_GROUP
,
777 EGG_DESKTOP_FILE_KEY_TERMINAL
,
780 GError
*terminal_error
= NULL
;
781 gboolean use_terminal
=
782 g_key_file_get_boolean (desktop_file
->key_file
,
783 EGG_DESKTOP_FILE_GROUP
,
784 EGG_DESKTOP_FILE_KEY_TERMINAL
,
789 g_propagate_error (error
, terminal_error
);
795 gs
= g_string_new ("xdg-terminal ");
796 append_quoted_word (gs
, command
, FALSE
, FALSE
);
798 command
= g_string_free (gs
, FALSE
);
806 translate_document_list (EggDesktopFile
*desktop_file
, GSList
*documents
)
808 gboolean accepts_uris
= egg_desktop_file_accepts_uris (desktop_file
);
811 for (d
= documents
, ret
= NULL
; d
; d
= d
->next
)
813 const char *document
= d
->data
;
814 gboolean is_uri
= !g_path_is_absolute (document
);
820 translated
= g_strdup (document
);
822 translated
= g_filename_to_uri (document
, NULL
, NULL
);
827 translated
= g_filename_from_uri (document
, NULL
, NULL
);
829 translated
= g_strdup (document
);
833 ret
= g_slist_prepend (ret
, translated
);
836 return g_slist_reverse (ret
);
840 free_document_list (GSList
*documents
)
844 for (d
= documents
; d
; d
= d
->next
)
846 g_slist_free (documents
);
850 * egg_desktop_file_parse_exec:
851 * @desktop_file: a #EggDesktopFile
852 * @documents: a list of document paths or URIs
853 * @error: error pointer
855 * Parses @desktop_file's Exec key, inserting @documents into it, and
856 * returns the result.
858 * If @documents contains non-file: URIs and @desktop_file does not
859 * accept URIs, those URIs will be ignored. Likewise, if @documents
860 * contains more elements than @desktop_file accepts, the extra
861 * documents will be ignored.
863 * Return value: the parsed Exec string
866 egg_desktop_file_parse_exec (EggDesktopFile
*desktop_file
,
870 GSList
*translated
, *docs
;
873 docs
= translated
= translate_document_list (desktop_file
, documents
);
874 command
= parse_exec (desktop_file
, &docs
, error
);
875 free_document_list (translated
);
881 parse_link (EggDesktopFile
*desktop_file
,
882 EggDesktopFile
**app_desktop_file
,
889 url
= g_key_file_get_string (desktop_file
->key_file
,
890 EGG_DESKTOP_FILE_GROUP
,
891 EGG_DESKTOP_FILE_KEY_URL
,
895 *documents
= g_slist_prepend (NULL
, url
);
897 /* FIXME: use gvfs */
898 key_file
= g_key_file_new ();
899 g_key_file_set_string (key_file
, EGG_DESKTOP_FILE_GROUP
,
900 EGG_DESKTOP_FILE_KEY_NAME
,
902 g_key_file_set_string (key_file
, EGG_DESKTOP_FILE_GROUP
,
903 EGG_DESKTOP_FILE_KEY_TYPE
,
905 g_key_file_set_string (key_file
, EGG_DESKTOP_FILE_GROUP
,
906 EGG_DESKTOP_FILE_KEY_EXEC
,
908 *app_desktop_file
= egg_desktop_file_new_from_key_file (key_file
, NULL
, NULL
);
913 start_startup_notification (GdkDisplay
*display
,
914 EggDesktopFile
*desktop_file
,
920 static int sequence
= 0;
922 char *description
, *wmclass
;
923 char *screen_str
, *workspace_str
;
925 if (g_key_file_has_key (desktop_file
->key_file
,
926 EGG_DESKTOP_FILE_GROUP
,
927 EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY
,
930 if (!g_key_file_get_boolean (desktop_file
->key_file
,
931 EGG_DESKTOP_FILE_GROUP
,
932 EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY
,
939 wmclass
= g_key_file_get_string (desktop_file
->key_file
,
940 EGG_DESKTOP_FILE_GROUP
,
941 EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS
,
947 if (launch_time
== (guint32
)-1)
948 launch_time
= gdk_x11_display_get_user_time (display
);
949 startup_id
= g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
951 (unsigned long)getpid (),
955 (unsigned long)launch_time
);
957 description
= g_strdup_printf (_("Starting %s"), desktop_file
->name
);
958 screen_str
= g_strdup_printf ("%d", screen
);
959 workspace_str
= workspace
== -1 ? NULL
: g_strdup_printf ("%d", workspace
);
961 gdk_x11_display_broadcast_startup_message (display
, "new",
963 "NAME", desktop_file
->name
,
964 "SCREEN", screen_str
,
966 "ICON", desktop_file
->icon
,
967 "DESKTOP", workspace_str
,
968 "DESCRIPTION", description
,
972 g_free (description
);
975 g_free (workspace_str
);
981 end_startup_notification (GdkDisplay
*display
,
982 const char *startup_id
)
984 gdk_x11_display_broadcast_startup_message (display
, "remove",
989 #define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */)
994 } StartupNotificationData
;
997 startup_notification_timeout (gpointer data
)
999 StartupNotificationData
*sn_data
= data
;
1001 end_startup_notification (sn_data
->display
, sn_data
->startup_id
);
1002 g_object_unref (sn_data
->display
);
1003 g_free (sn_data
->startup_id
);
1010 set_startup_notification_timeout (GdkDisplay
*display
,
1011 const char *startup_id
)
1013 StartupNotificationData
*sn_data
;
1015 sn_data
= g_new (StartupNotificationData
, 1);
1016 sn_data
->display
= g_object_ref (display
);
1017 sn_data
->startup_id
= g_strdup (startup_id
);
1019 g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH
,
1020 startup_notification_timeout
, sn_data
);
1024 array_putenv (GPtrArray
*env
, char *variable
)
1032 env
= g_ptr_array_new ();
1034 envp
= g_listenv ();
1035 for (i
= 0; envp
[i
]; i
++)
1039 value
= g_getenv (envp
[i
]);
1040 g_ptr_array_add (env
, g_strdup_printf ("%s=%s", envp
[i
],
1041 value
? value
: ""));
1046 keylen
= strcspn (variable
, "=");
1048 /* Remove old value of key */
1049 for (i
= 0; i
< env
->len
; i
++)
1051 char *envvar
= env
->pdata
[i
];
1053 if (!strncmp (envvar
, variable
, keylen
) && envvar
[keylen
] == '=')
1056 g_ptr_array_remove_index_fast (env
, i
);
1062 g_ptr_array_add (env
, g_strdup (variable
));
1068 egg_desktop_file_launchv (EggDesktopFile
*desktop_file
,
1069 GSList
*documents
, va_list args
,
1072 EggDesktopFileLaunchOption option
;
1073 GSList
*translated_documents
= NULL
, *docs
= NULL
;
1074 char *command
, **argv
;
1075 int argc
, i
, screen_num
;
1076 gboolean success
, current_success
;
1077 GdkDisplay
*display
;
1080 GPtrArray
*env
= NULL
;
1081 char **variables
= NULL
;
1082 GdkScreen
*screen
= NULL
;
1084 const char *directory
= NULL
;
1085 guint32 launch_time
= (guint32
)-1;
1086 GSpawnFlags flags
= G_SPAWN_SEARCH_PATH
;
1087 GSpawnChildSetupFunc setup_func
= NULL
;
1088 gpointer setup_data
= NULL
;
1090 GPid
*ret_pid
= NULL
;
1091 int *ret_stdin
= NULL
, *ret_stdout
= NULL
, *ret_stderr
= NULL
;
1092 char **ret_startup_id
= NULL
;
1094 if (documents
&& desktop_file
->document_code
== 0)
1096 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
1097 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE
,
1098 _("Application does not accept documents on command line"));
1102 /* Read the options: technically it's incorrect for the caller to
1103 * NULL-terminate the list of options (rather than 0-terminating
1104 * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
1105 * it's more consistent with other glib/gtk methods, and it will
1106 * work as long as sizeof (int) <= sizeof (NULL), and NULL is
1107 * represented as 0. (Which is true everywhere we care about.)
1109 while ((option
= va_arg (args
, EggDesktopFileLaunchOption
)))
1113 case EGG_DESKTOP_FILE_LAUNCH_CLEARENV
:
1115 g_ptr_array_free (env
, TRUE
);
1116 env
= g_ptr_array_new ();
1118 case EGG_DESKTOP_FILE_LAUNCH_PUTENV
:
1119 variables
= va_arg (args
, char **);
1120 for (i
= 0; variables
[i
]; i
++)
1121 env
= array_putenv (env
, variables
[i
]);
1124 case EGG_DESKTOP_FILE_LAUNCH_SCREEN
:
1125 screen
= va_arg (args
, GdkScreen
*);
1127 case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE
:
1128 workspace
= va_arg (args
, int);
1131 case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY
:
1132 directory
= va_arg (args
, const char *);
1134 case EGG_DESKTOP_FILE_LAUNCH_TIME
:
1135 launch_time
= va_arg (args
, guint32
);
1137 case EGG_DESKTOP_FILE_LAUNCH_FLAGS
:
1138 flags
|= va_arg (args
, GSpawnFlags
);
1139 /* Make sure they didn't set any flags that don't make sense. */
1140 flags
&= ~G_SPAWN_FILE_AND_ARGV_ZERO
;
1142 case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC
:
1143 setup_func
= va_arg (args
, GSpawnChildSetupFunc
);
1144 setup_data
= va_arg (args
, gpointer
);
1147 case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID
:
1148 ret_pid
= va_arg (args
, GPid
*);
1150 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE
:
1151 ret_stdin
= va_arg (args
, int *);
1153 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE
:
1154 ret_stdout
= va_arg (args
, int *);
1156 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE
:
1157 ret_stderr
= va_arg (args
, int *);
1159 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID
:
1160 ret_startup_id
= va_arg (args
, char **);
1164 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
1165 EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION
,
1166 _("Unrecognized launch option: %d"),
1167 GPOINTER_TO_INT (option
));
1175 char *display_name
= gdk_screen_make_display_name (screen
);
1176 char *display_env
= g_strdup_printf ("DISPLAY=%s", display_name
);
1177 env
= array_putenv (env
, display_env
);
1178 g_free (display_name
);
1179 g_free (display_env
);
1181 display
= gdk_screen_get_display (screen
);
1185 display
= gdk_display_get_default ();
1186 screen
= gdk_display_get_default_screen (display
);
1188 screen_num
= gdk_screen_get_number (screen
);
1190 translated_documents
= translate_document_list (desktop_file
, documents
);
1191 docs
= translated_documents
;
1197 command
= parse_exec (desktop_file
, &docs
, error
);
1201 if (!g_shell_parse_argv (command
, &argc
, &argv
, error
))
1208 startup_id
= start_startup_notification (display
, desktop_file
,
1209 argv
[0], screen_num
,
1210 workspace
, launch_time
);
1213 char *startup_id_env
= g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
1215 env
= array_putenv (env
, startup_id_env
);
1216 g_free (startup_id_env
);
1220 g_ptr_array_add (env
, NULL
);
1223 g_spawn_async_with_pipes (directory
,
1225 env
? (char **)(env
->pdata
) : NULL
,
1227 setup_func
, setup_data
,
1229 ret_stdin
, ret_stdout
, ret_stderr
,
1235 if (current_success
)
1237 set_startup_notification_timeout (display
, startup_id
);
1240 *ret_startup_id
= startup_id
;
1242 g_free (startup_id
);
1245 g_free (startup_id
);
1247 else if (ret_startup_id
)
1248 *ret_startup_id
= NULL
;
1250 if (current_success
)
1252 /* If we successfully launch any instances of the app, make
1253 * sure we return TRUE and don't set @error.
1258 /* Also, only set the output params on the first one */
1260 ret_stdin
= ret_stdout
= ret_stderr
= NULL
;
1261 ret_startup_id
= NULL
;
1264 while (docs
&& current_success
);
1269 g_ptr_array_foreach (env
, (GFunc
)g_free
, NULL
);
1270 g_ptr_array_free (env
, TRUE
);
1272 free_document_list (translated_documents
);
1278 * egg_desktop_file_launch:
1279 * @desktop_file: an #EggDesktopFile
1280 * @documents: a list of URIs or paths to documents to open
1281 * @error: error pointer
1282 * @...: additional options
1284 * Launches @desktop_file with the given arguments. Additional options
1285 * can be specified as follows:
1287 * %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
1288 * clears the environment in the child process
1289 * %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
1290 * adds the NAME=VALUE strings in the given %NULL-terminated
1291 * array to the child process's environment
1292 * %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
1293 * causes the application to be launched on the given screen
1294 * %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
1295 * causes the application to be launched on the given workspace
1296 * %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
1297 * causes the application to be launched in the given directory
1298 * %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
1299 * sets the "launch time" for the application. If the user
1300 * interacts with another window after @launch_time but before
1301 * the launched application creates its first window, the window
1302 * manager may choose to not give focus to the new application.
1303 * Passing 0 for @launch_time will explicitly request that the
1304 * application not receive focus.
1305 * %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
1306 * Sets additional #GSpawnFlags to use. See g_spawn_async() for
1308 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
1309 * Sets the child setup callback and the data to pass to it.
1310 * (See g_spawn_async() for more details.)
1312 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
1313 * On a successful launch, sets *@pid to the PID of the launched
1315 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
1316 * On a successful launch, sets *@startup_id to the Startup
1317 * Notification "startup id" of the launched application.
1318 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
1319 * On a successful launch, sets *@fd to the file descriptor of
1320 * a pipe connected to the application's stdin.
1321 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
1322 * On a successful launch, sets *@fd to the file descriptor of
1323 * a pipe connected to the application's stdout.
1324 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
1325 * On a successful launch, sets *@fd to the file descriptor of
1326 * a pipe connected to the application's stderr.
1328 * The options should be terminated with a single %NULL.
1330 * If @documents contains multiple documents, but
1331 * egg_desktop_file_accepts_multiple() returns %FALSE for
1332 * @desktop_file, then egg_desktop_file_launch() will actually launch
1333 * multiple instances of the application. In that case, the return
1334 * value (as well as any values passed via
1335 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
1336 * first instance of the application that was launched (but the
1337 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
1340 * Return value: %TRUE if the application was successfully launched.
1343 egg_desktop_file_launch (EggDesktopFile
*desktop_file
,
1344 GSList
*documents
, GError
**error
,
1349 EggDesktopFile
*app_desktop_file
;
1351 switch (desktop_file
->type
)
1353 case EGG_DESKTOP_FILE_TYPE_APPLICATION
:
1354 va_start (args
, error
);
1355 success
= egg_desktop_file_launchv (desktop_file
, documents
,
1360 case EGG_DESKTOP_FILE_TYPE_LINK
:
1363 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
1364 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE
,
1365 _("Can't pass document URIs to a 'Type=Link' desktop entry"));
1369 if (!parse_link (desktop_file
, &app_desktop_file
, &documents
, error
))
1372 va_start (args
, error
);
1373 success
= egg_desktop_file_launchv (app_desktop_file
, documents
,
1377 egg_desktop_file_free (app_desktop_file
);
1378 free_document_list (documents
);
1381 case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED
:
1382 case EGG_DESKTOP_FILE_TYPE_DIRECTORY
:
1384 g_set_error (error
, EGG_DESKTOP_FILE_ERROR
,
1385 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE
,
1386 _("Not a launchable item"));
1396 egg_desktop_file_error_quark (void)
1398 return g_quark_from_static_string ("egg-desktop_file-error-quark");
1402 G_LOCK_DEFINE_STATIC (egg_desktop_file
);
1403 static EggDesktopFile
*egg_desktop_file
;
1406 egg_set_desktop_file_internal (const char *desktop_file_path
,
1407 gboolean set_defaults
)
1409 GError
*error
= NULL
;
1411 G_LOCK (egg_desktop_file
);
1412 if (egg_desktop_file
)
1413 egg_desktop_file_free (egg_desktop_file
);
1415 egg_desktop_file
= egg_desktop_file_new (desktop_file_path
, &error
);
1418 g_warning ("Could not load desktop file '%s': %s",
1419 desktop_file_path
, error
->message
);
1420 g_error_free (error
);
1423 if (set_defaults
&& egg_desktop_file
!= NULL
) {
1424 /* Set localized application name and default window icon */
1425 if (egg_desktop_file
->name
)
1426 g_set_application_name (egg_desktop_file
->name
);
1427 if (egg_desktop_file
->icon
)
1429 if (g_path_is_absolute (egg_desktop_file
->icon
))
1430 gtk_window_set_default_icon_from_file (egg_desktop_file
->icon
, NULL
);
1432 gtk_window_set_default_icon_name (egg_desktop_file
->icon
);
1436 G_UNLOCK (egg_desktop_file
);
1440 * egg_set_desktop_file:
1441 * @desktop_file_path: path to the application's desktop file
1443 * Creates an #EggDesktopFile for the application from the data at
1444 * @desktop_file_path. This will also call g_set_application_name()
1445 * with the localized application name from the desktop file, and
1446 * gtk_window_set_default_icon_name() or
1447 * gtk_window_set_default_icon_from_file() with the application's
1448 * icon. Other code may use additional information from the desktop
1450 * See egg_set_desktop_file_without_defaults() for a variant of this
1451 * function that does not set the application name and default window
1454 * Note that for thread safety reasons, this function can only
1455 * be called once, and is mutually exclusive with calling
1456 * egg_set_desktop_file_without_defaults().
1459 egg_set_desktop_file (const char *desktop_file_path
)
1461 egg_set_desktop_file_internal (desktop_file_path
, TRUE
);
1465 * egg_set_desktop_file_without_defaults:
1466 * @desktop_file_path: path to the application's desktop file
1468 * Creates an #EggDesktopFile for the application from the data at
1469 * @desktop_file_path.
1470 * See egg_set_desktop_file() for a variant of this function that
1471 * sets the application name and default window icon from the information
1472 * in the desktop file.
1474 * Note that for thread safety reasons, this function can only
1475 * be called once, and is mutually exclusive with calling
1476 * egg_set_desktop_file().
1479 egg_set_desktop_file_without_defaults (const char *desktop_file_path
)
1481 egg_set_desktop_file_internal (desktop_file_path
, FALSE
);
1485 * egg_get_desktop_file:
1487 * Gets the application's #EggDesktopFile, as set by
1488 * egg_set_desktop_file().
1490 * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
1493 egg_get_desktop_file (void)
1495 EggDesktopFile
*retval
;
1497 G_LOCK (egg_desktop_file
);
1498 retval
= egg_desktop_file
;
1499 G_UNLOCK (egg_desktop_file
);