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 "eggdesktopfile.h"
33 #include <glib/gi18n.h>
35 #include <gtk/gtkwindow.h>
38 struct EggDesktopFile {
43 EggDesktopFileType type;
48 * egg_desktop_file_new:
49 * @desktop_file_path: path to a Freedesktop-style Desktop file
50 * @error: error pointer
52 * Creates a new #EggDesktopFile for @desktop_file.
54 * Return value: the new #EggDesktopFile, or %NULL on error.
57 egg_desktop_file_new (const char *desktop_file_path, GError **error)
59 EggDesktopFile *desktop_file;
62 key_file = g_key_file_new ();
63 if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
65 g_key_file_free (key_file);
69 desktop_file = egg_desktop_file_new_from_key_file (key_file,
73 g_key_file_free (key_file);
79 * egg_desktop_file_new_from_data_dirs:
80 * @desktop_file_path: relative path to a Freedesktop-style Desktop file
81 * @error: error pointer
83 * Looks for @desktop_file_path in the paths returned from
84 * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
85 * a new #EggDesktopFile from it.
87 * Return value: the new #EggDesktopFile, or %NULL on error.
90 egg_desktop_file_new_from_data_dirs (const char *desktop_file_path,
93 EggDesktopFile *desktop_file;
97 key_file = g_key_file_new ();
98 if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
99 &full_path, 0, error))
101 g_key_file_free (key_file);
105 desktop_file = egg_desktop_file_new_from_key_file (key_file,
110 g_key_file_free (key_file);
116 * egg_desktop_file_new_from_key_file:
117 * @key_file: a #GKeyFile representing a desktop file
118 * @source: the path or URI that @key_file was loaded from, or %NULL
119 * @error: error pointer
121 * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
122 * @key_file on success (meaning it will be freed when the desktop_file
125 * Return value: the new #EggDesktopFile, or %NULL on error.
128 egg_desktop_file_new_from_key_file (GKeyFile *key_file,
132 EggDesktopFile *desktop_file;
133 char *version, *type;
135 if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
137 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
138 EGG_DESKTOP_FILE_ERROR_INVALID,
139 _("File is not a valid .desktop file"));
143 version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
144 EGG_DESKTOP_FILE_KEY_VERSION,
151 version_num = g_ascii_strtod (version, &end);
154 g_warning ("Invalid Version string '%s' in %s",
155 version, source ? source : "(unknown)");
157 else if (version_num > 1.0)
159 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
160 EGG_DESKTOP_FILE_ERROR_INVALID,
161 _("Unrecognized desktop file Version '%s'"), version);
169 desktop_file = g_new0 (EggDesktopFile, 1);
171 if (g_path_is_absolute (source))
172 desktop_file->source = g_filename_to_uri (source, NULL, NULL);
174 desktop_file->source = g_strdup (source);
176 desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
177 EGG_DESKTOP_FILE_KEY_NAME, error);
178 if (!desktop_file->name)
180 egg_desktop_file_free (desktop_file);
184 type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
185 EGG_DESKTOP_FILE_KEY_TYPE, error);
188 egg_desktop_file_free (desktop_file);
192 if (!strcmp (type, "Application"))
196 desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
198 exec = g_key_file_get_string (key_file,
199 EGG_DESKTOP_FILE_GROUP,
200 EGG_DESKTOP_FILE_KEY_EXEC,
204 egg_desktop_file_free (desktop_file);
209 /* See if it takes paths or URIs or neither */
210 for (p = exec; *p; p++)
214 if (p[1] == '\0' || strchr ("FfUu", p[1]))
216 desktop_file->document_code = p[1];
225 else if (!strcmp (type, "Link"))
229 desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
231 url = g_key_file_get_string (key_file,
232 EGG_DESKTOP_FILE_GROUP,
233 EGG_DESKTOP_FILE_KEY_URL,
237 egg_desktop_file_free (desktop_file);
243 else if (!strcmp (type, "Directory"))
244 desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
246 desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
250 /* Check the Icon key */
251 desktop_file->icon = g_key_file_get_string (key_file,
252 EGG_DESKTOP_FILE_GROUP,
253 EGG_DESKTOP_FILE_KEY_ICON,
255 if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
259 /* Lots of .desktop files still get this wrong */
260 ext = strrchr (desktop_file->icon, '.');
261 if (ext && (!strcmp (ext, ".png") ||
262 !strcmp (ext, ".xpm") ||
263 !strcmp (ext, ".svg")))
265 g_warning ("Desktop file '%s' has malformed Icon key '%s'"
266 "(should not include extension)",
267 source ? source : "(unknown)",
273 desktop_file->key_file = key_file;
278 * egg_desktop_file_free:
279 * @desktop_file: an #EggDesktopFile
281 * Frees @desktop_file.
284 egg_desktop_file_free (EggDesktopFile *desktop_file)
286 g_key_file_free (desktop_file->key_file);
287 g_free (desktop_file->source);
288 g_free (desktop_file->name);
289 g_free (desktop_file->icon);
290 g_free (desktop_file);
294 * egg_desktop_file_get_key_file:
295 * @desktop_file: an #EggDesktopFile
297 * Gets the #GKeyFile associated with @desktop_file. You must not free
298 * this value, and changes made to it will not be reflected by
301 * Return value: the #GKeyFile associated with @desktop_file.
304 egg_desktop_file_get_key_file (EggDesktopFile *desktop_file)
306 return desktop_file->key_file;
310 * egg_desktop_file_get_source:
311 * @desktop_file: an #EggDesktopFile
313 * Gets the URI that @desktop_file was loaded from.
315 * Return value: @desktop_file's source URI
318 egg_desktop_file_get_source (EggDesktopFile *desktop_file)
320 return desktop_file->source;
324 * egg_desktop_file_get_desktop_file_type:
325 * @desktop_file: an #EggDesktopFile
327 * Gets the desktop file type of @desktop_file.
329 * Return value: @desktop_file's type
332 egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
334 return desktop_file->type;
338 * egg_desktop_file_get_name:
339 * @desktop_file: an #EggDesktopFile
341 * Gets the (localized) value of @desktop_file's "Name" key.
343 * Return value: the application/link name
346 egg_desktop_file_get_name (EggDesktopFile *desktop_file)
348 return desktop_file->name;
352 * egg_desktop_file_get_icon:
353 * @desktop_file: an #EggDesktopFile
355 * Gets the value of @desktop_file's "Icon" key.
357 * If the icon string is a full path (that is, if g_path_is_absolute()
358 * returns %TRUE when called on it), it points to a file containing an
359 * unthemed icon. If the icon string is not a full path, it is the
360 * name of a themed icon, which can be looked up with %GtkIconTheme,
361 * or passed directly to a theme-aware widget like %GtkImage or
362 * %GtkCellRendererPixbuf.
364 * Return value: the icon path or name
367 egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
369 return desktop_file->icon;
373 * egg_desktop_file_can_launch:
374 * @desktop_file: an #EggDesktopFile
375 * @desktop_environment: the name of the running desktop environment,
378 * Tests if @desktop_file can/should be launched in the current
379 * environment. If @desktop_environment is non-%NULL, @desktop_file's
380 * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
381 * this desktop_file is appropriate for the named environment.
383 * Furthermore, if @desktop_file has type
384 * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
385 * also checked, to make sure the binary it points to exists.
387 * egg_desktop_file_can_launch() does NOT check the value of the
390 * Return value: %TRUE if @desktop_file can be launched
393 egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
394 const char *desktop_environment)
396 char *try_exec, *found_program;
397 char **only_show_in, **not_show_in;
401 if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
402 desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
405 if (desktop_environment)
407 only_show_in = g_key_file_get_string_list (desktop_file->key_file,
408 EGG_DESKTOP_FILE_GROUP,
409 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
413 for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
415 if (!strcmp (only_show_in[i], desktop_environment))
419 g_strfreev (only_show_in);
425 not_show_in = g_key_file_get_string_list (desktop_file->key_file,
426 EGG_DESKTOP_FILE_GROUP,
427 EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
431 for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
433 if (!strcmp (not_show_in[i], desktop_environment))
437 g_strfreev (not_show_in);
444 if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
446 try_exec = g_key_file_get_string (desktop_file->key_file,
447 EGG_DESKTOP_FILE_GROUP,
448 EGG_DESKTOP_FILE_KEY_TRY_EXEC,
452 found_program = g_find_program_in_path (try_exec);
457 g_free (found_program);
465 * egg_desktop_file_accepts_documents:
466 * @desktop_file: an #EggDesktopFile
468 * Tests if @desktop_file represents an application that can accept
469 * documents on the command line.
471 * Return value: %TRUE or %FALSE
474 egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
476 return desktop_file->document_code != 0;
480 * egg_desktop_file_accepts_multiple:
481 * @desktop_file: an #EggDesktopFile
483 * Tests if @desktop_file can accept multiple documents at once.
485 * If this returns %FALSE, you can still pass multiple documents to
486 * egg_desktop_file_launch(), but that will result in multiple copies
487 * of the application being launched. See egg_desktop_file_launch()
490 * Return value: %TRUE or %FALSE
493 egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
495 return (desktop_file->document_code == 'F' ||
496 desktop_file->document_code == 'U');
500 * egg_desktop_file_accepts_uris:
501 * @desktop_file: an #EggDesktopFile
503 * Tests if @desktop_file can accept (non-"file:") URIs as documents to
506 * Return value: %TRUE or %FALSE
509 egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
511 return (desktop_file->document_code == 'U' ||
512 desktop_file->document_code == 'u');
516 append_quoted_word (GString *str,
518 gboolean in_single_quotes,
519 gboolean in_double_quotes)
523 if (!in_single_quotes && !in_double_quotes)
524 g_string_append_c (str, '\'');
525 else if (!in_single_quotes && in_double_quotes)
526 g_string_append (str, "\"'");
528 if (!strchr (s, '\''))
529 g_string_append (str, s);
532 for (p = s; *p != '\0'; p++)
535 g_string_append (str, "'\\''");
537 g_string_append_c (str, *p);
541 if (!in_single_quotes && !in_double_quotes)
542 g_string_append_c (str, '\'');
543 else if (!in_single_quotes && in_double_quotes)
544 g_string_append (str, "'\"");
548 do_percent_subst (EggDesktopFile *desktop_file,
552 gboolean in_single_quotes,
553 gboolean in_double_quotes)
561 g_string_append_c (str, '%');
566 for (d = *documents; d; d = d->next)
569 g_string_append (str, " ");
570 append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
579 doc = (*documents)->data;
580 g_string_append (str, " ");
581 append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
582 *documents = (*documents)->next;
587 if (desktop_file->icon)
589 g_string_append (str, "--icon ");
590 append_quoted_word (str, desktop_file->icon,
591 in_single_quotes, in_double_quotes);
596 if (desktop_file->name)
598 append_quoted_word (str, desktop_file->name,
599 in_single_quotes, in_double_quotes);
604 if (desktop_file->source)
606 append_quoted_word (str, desktop_file->source,
607 in_single_quotes, in_double_quotes);
617 /* Deprecated; skip */
621 g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
627 parse_exec (EggDesktopFile *desktop_file,
631 char *exec, *p, *command;
632 gboolean escape, single_quot, double_quot;
635 exec = g_key_file_get_string (desktop_file->key_file,
636 EGG_DESKTOP_FILE_GROUP,
637 EGG_DESKTOP_FILE_KEY_EXEC,
642 /* Build the command */
643 gs = g_string_new (NULL);
644 escape = single_quot = double_quot = FALSE;
646 for (p = exec; *p != '\0'; p++)
651 g_string_append_c (gs, *p);
657 g_string_append_c (gs, *p);
661 g_string_append_c (gs, *p);
662 if (!single_quot && !double_quot)
664 else if (single_quot)
669 g_string_append_c (gs, *p);
670 if (!single_quot && !double_quot)
672 else if (double_quot)
675 else if (*p == '%' && p[1])
677 do_percent_subst (desktop_file, p[1], gs, documents,
678 single_quot, double_quot);
682 g_string_append_c (gs, *p);
686 command = g_string_free (gs, FALSE);
688 /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
689 if (g_key_file_has_key (desktop_file->key_file,
690 EGG_DESKTOP_FILE_GROUP,
691 EGG_DESKTOP_FILE_KEY_TERMINAL,
694 GError *terminal_error = NULL;
695 gboolean use_terminal =
696 g_key_file_get_boolean (desktop_file->key_file,
697 EGG_DESKTOP_FILE_GROUP,
698 EGG_DESKTOP_FILE_KEY_TERMINAL,
703 g_propagate_error (error, terminal_error);
709 gs = g_string_new ("xdg-terminal ");
710 append_quoted_word (gs, command, FALSE, FALSE);
712 command = g_string_free (gs, FALSE);
720 translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
722 gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
725 for (d = documents, ret = NULL; d; d = d->next)
727 const char *document = d->data;
728 gboolean is_uri = !g_path_is_absolute (document);
734 translated = g_strdup (document);
736 translated = g_filename_to_uri (document, NULL, NULL);
741 translated = g_filename_from_uri (document, NULL, NULL);
743 translated = g_strdup (document);
747 ret = g_slist_prepend (ret, translated);
750 return g_slist_reverse (ret);
754 free_document_list (GSList *documents)
758 for (d = documents; d; d = d->next)
760 g_slist_free (documents);
764 * egg_desktop_file_parse_exec:
765 * @desktop_file: a #EggDesktopFile
766 * @documents: a list of document paths or URIs
767 * @error: error pointer
769 * Parses @desktop_file's Exec key, inserting @documents into it, and
770 * returns the result.
772 * If @documents contains non-file: URIs and @desktop_file does not
773 * accept URIs, those URIs will be ignored. Likewise, if @documents
774 * contains more elements than @desktop_file accepts, the extra
775 * documents will be ignored.
777 * Return value: the parsed Exec string
780 egg_desktop_file_parse_exec (EggDesktopFile *desktop_file,
784 GSList *translated, *docs;
787 docs = translated = translate_document_list (desktop_file, documents);
788 command = parse_exec (desktop_file, &docs, error);
789 free_document_list (translated);
795 parse_link (EggDesktopFile *desktop_file,
796 EggDesktopFile **app_desktop_file,
803 url = g_key_file_get_string (desktop_file->key_file,
804 EGG_DESKTOP_FILE_GROUP,
805 EGG_DESKTOP_FILE_KEY_URL,
809 *documents = g_slist_prepend (NULL, url);
811 /* FIXME: use gvfs */
812 key_file = g_key_file_new ();
813 g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
814 EGG_DESKTOP_FILE_KEY_NAME,
816 g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
817 EGG_DESKTOP_FILE_KEY_TYPE,
819 g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
820 EGG_DESKTOP_FILE_KEY_EXEC,
822 *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
826 #ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
828 start_startup_notification (GdkDisplay *display,
829 EggDesktopFile *desktop_file,
835 static int sequence = 0;
837 char *description, *wmclass;
838 char *screen_str, *workspace_str;
840 if (g_key_file_has_key (desktop_file->key_file,
841 EGG_DESKTOP_FILE_GROUP,
842 EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
845 if (!g_key_file_get_boolean (desktop_file->key_file,
846 EGG_DESKTOP_FILE_GROUP,
847 EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
854 wmclass = g_key_file_get_string (desktop_file->key_file,
855 EGG_DESKTOP_FILE_GROUP,
856 EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
862 if (launch_time == (guint32)-1)
863 launch_time = gdk_x11_display_get_user_time (display);
864 startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
866 (unsigned long)getpid (),
870 (unsigned long)launch_time);
872 description = g_strdup_printf (_("Starting %s"), desktop_file->name);
873 screen_str = g_strdup_printf ("%d", screen);
874 workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
876 gdk_x11_display_broadcast_startup_message (display, "new",
878 "NAME", desktop_file->name,
879 "SCREEN", screen_str,
881 "ICON", desktop_file->icon,
882 "DESKTOP", workspace_str,
883 "DESCRIPTION", description,
887 g_free (description);
890 g_free (workspace_str);
896 end_startup_notification (GdkDisplay *display,
897 const char *startup_id)
899 gdk_x11_display_broadcast_startup_message (display, "remove",
904 #define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
909 } StartupNotificationData;
912 startup_notification_timeout (gpointer data)
914 StartupNotificationData *sn_data = data;
916 end_startup_notification (sn_data->display, sn_data->startup_id);
917 g_object_unref (sn_data->display);
918 g_free (sn_data->startup_id);
925 set_startup_notification_timeout (GdkDisplay *display,
926 const char *startup_id)
928 StartupNotificationData *sn_data;
930 sn_data = g_new (StartupNotificationData, 1);
931 sn_data->display = g_object_ref (display);
932 sn_data->startup_id = g_strdup (startup_id);
934 g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
935 startup_notification_timeout, sn_data);
937 #endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
939 extern char **environ;
942 array_putenv (GPtrArray *env, char *variable)
948 env = g_ptr_array_new ();
950 for (i = 0; environ[i]; i++)
951 g_ptr_array_add (env, g_strdup (environ[i]));
954 keylen = strcspn (variable, "=");
956 /* Remove old value of key */
957 for (i = 0; i < env->len; i++)
959 char *envvar = env->pdata[i];
961 if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
964 g_ptr_array_remove_index_fast (env, i);
970 g_ptr_array_add (env, g_strdup (variable));
976 egg_desktop_file_launchv (EggDesktopFile *desktop_file,
977 GSList *documents, va_list args,
980 EggDesktopFileLaunchOption option;
981 GSList *translated_documents, *docs;
982 char *command, **argv;
983 int argc, i, screen_num;
984 gboolean success, current_success;
988 GPtrArray *env = NULL;
989 char **variables = NULL;
990 GdkScreen *screen = NULL;
992 const char *directory = NULL;
993 guint32 launch_time = (guint32)-1;
994 GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
995 GSpawnChildSetupFunc setup_func = NULL;
996 gpointer setup_data = NULL;
998 GPid *ret_pid = NULL;
999 int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
1000 char **ret_startup_id = NULL;
1002 if (documents && desktop_file->document_code == 0)
1004 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1005 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1006 _("Application does not accept documents on command line"));
1010 /* Read the options: technically it's incorrect for the caller to
1011 * NULL-terminate the list of options (rather than 0-terminating
1012 * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
1013 * it's more consistent with other glib/gtk methods, and it will
1014 * work as long as sizeof (int) <= sizeof (NULL), and NULL is
1015 * represented as 0. (Which is true everywhere we care about.)
1017 while ((option = va_arg (args, EggDesktopFileLaunchOption)))
1021 case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
1023 g_ptr_array_free (env, TRUE);
1024 env = g_ptr_array_new ();
1026 case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
1027 variables = va_arg (args, char **);
1028 for (i = 0; variables[i]; i++)
1029 env = array_putenv (env, variables[i]);
1032 case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
1033 screen = va_arg (args, GdkScreen *);
1035 case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
1036 workspace = va_arg (args, int);
1039 case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
1040 directory = va_arg (args, const char *);
1042 case EGG_DESKTOP_FILE_LAUNCH_TIME:
1043 launch_time = va_arg (args, guint32);
1045 case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
1046 flags |= va_arg (args, GSpawnFlags);
1047 /* Make sure they didn't set any flags that don't make sense. */
1048 flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
1050 case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
1051 setup_func = va_arg (args, GSpawnChildSetupFunc);
1052 setup_data = va_arg (args, gpointer);
1055 case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
1056 ret_pid = va_arg (args, GPid *);
1058 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
1059 ret_stdin = va_arg (args, int *);
1061 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
1062 ret_stdout = va_arg (args, int *);
1064 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
1065 ret_stderr = va_arg (args, int *);
1067 case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
1068 ret_startup_id = va_arg (args, char **);
1072 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1073 EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
1074 _("Unrecognized launch option: %d"),
1075 GPOINTER_TO_INT (option));
1083 char *display_name = gdk_screen_make_display_name (screen);
1084 char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
1085 env = array_putenv (env, display_env);
1086 g_free (display_name);
1087 g_free (display_env);
1089 display = gdk_screen_get_display (screen);
1093 display = gdk_display_get_default ();
1094 screen = gdk_display_get_default_screen (display);
1096 screen_num = gdk_screen_get_number (screen);
1098 translated_documents = translate_document_list (desktop_file, documents);
1099 docs = translated_documents;
1105 command = parse_exec (desktop_file, &docs, error);
1109 if (!g_shell_parse_argv (command, &argc, &argv, error))
1116 #ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
1117 startup_id = start_startup_notification (display, desktop_file,
1118 argv[0], screen_num,
1119 workspace, launch_time);
1122 char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
1124 env = array_putenv (env, startup_id_env);
1125 g_free (startup_id_env);
1129 #endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
1132 g_spawn_async_with_pipes (directory,
1134 env ? (char **)(env->pdata) : NULL,
1136 setup_func, setup_data,
1138 ret_stdin, ret_stdout, ret_stderr,
1144 #ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
1145 if (current_success)
1147 set_startup_notification_timeout (display, startup_id);
1150 *ret_startup_id = startup_id;
1152 g_free (startup_id);
1155 #endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
1156 g_free (startup_id);
1158 else if (ret_startup_id)
1159 *ret_startup_id = NULL;
1161 if (current_success)
1163 /* If we successfully launch any instances of the app, make
1164 * sure we return TRUE and don't set @error.
1169 /* Also, only set the output params on the first one */
1171 ret_stdin = ret_stdout = ret_stderr = NULL;
1172 ret_startup_id = NULL;
1175 while (docs && current_success);
1180 g_strfreev ((char **)env->pdata);
1181 g_ptr_array_free (env, FALSE);
1183 free_document_list (translated_documents);
1189 * egg_desktop_file_launch:
1190 * @desktop_file: an #EggDesktopFile
1191 * @documents: a list of URIs or paths to documents to open
1192 * @error: error pointer
1193 * @...: additional options
1195 * Launches @desktop_file with the given arguments. Additional options
1196 * can be specified as follows:
1198 * %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
1199 * clears the environment in the child process
1200 * %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
1201 * adds the NAME=VALUE strings in the given %NULL-terminated
1202 * array to the child process's environment
1203 * %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
1204 * causes the application to be launched on the given screen
1205 * %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
1206 * causes the application to be launched on the given workspace
1207 * %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
1208 * causes the application to be launched in the given directory
1209 * %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
1210 * sets the "launch time" for the application. If the user
1211 * interacts with another window after @launch_time but before
1212 * the launched application creates its first window, the window
1213 * manager may choose to not give focus to the new application.
1214 * Passing 0 for @launch_time will explicitly request that the
1215 * application not receive focus.
1216 * %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
1217 * Sets additional #GSpawnFlags to use. See g_spawn_async() for
1219 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
1220 * Sets the child setup callback and the data to pass to it.
1221 * (See g_spawn_async() for more details.)
1223 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
1224 * On a successful launch, sets *@pid to the PID of the launched
1226 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
1227 * On a successful launch, sets *@startup_id to the Startup
1228 * Notification "startup id" of the launched application.
1229 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
1230 * On a successful launch, sets *@fd to the file descriptor of
1231 * a pipe connected to the application's stdin.
1232 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
1233 * On a successful launch, sets *@fd to the file descriptor of
1234 * a pipe connected to the application's stdout.
1235 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
1236 * On a successful launch, sets *@fd to the file descriptor of
1237 * a pipe connected to the application's stderr.
1239 * The options should be terminated with a single %NULL.
1241 * If @documents contains multiple documents, but
1242 * egg_desktop_file_accepts_multiple() returns %FALSE for
1243 * @desktop_file, then egg_desktop_file_launch() will actually launch
1244 * multiple instances of the application. In that case, the return
1245 * value (as well as any values passed via
1246 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
1247 * first instance of the application that was launched (but the
1248 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
1251 * Return value: %TRUE if the application was successfully launched.
1254 egg_desktop_file_launch (EggDesktopFile *desktop_file,
1255 GSList *documents, GError **error,
1260 EggDesktopFile *app_desktop_file;
1262 switch (desktop_file->type)
1264 case EGG_DESKTOP_FILE_TYPE_APPLICATION:
1265 va_start (args, error);
1266 success = egg_desktop_file_launchv (desktop_file, documents,
1271 case EGG_DESKTOP_FILE_TYPE_LINK:
1274 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1275 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1276 _("Can't pass document URIs to a 'Type=Link' desktop entry"));
1280 if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
1283 va_start (args, error);
1284 success = egg_desktop_file_launchv (app_desktop_file, documents,
1288 egg_desktop_file_free (app_desktop_file);
1289 free_document_list (documents);
1293 g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1294 EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1295 _("Not a launchable item"));
1305 egg_desktop_file_error_quark (void)
1307 return g_quark_from_static_string ("egg-desktop_file-error-quark");
1311 G_LOCK_DEFINE_STATIC (egg_desktop_file);
1312 static EggDesktopFile *egg_desktop_file;
1315 * egg_set_desktop_file:
1316 * @desktop_file_path: path to the application's desktop file
1318 * Creates an #EggDesktopFile for the application from the data at
1319 * @desktop_file_path. This will also call g_set_application_name()
1320 * with the localized application name from the desktop file, and
1321 * gtk_window_set_default_icon_name() or
1322 * gtk_window_set_default_icon_from_file() with the application's
1323 * icon. Other code may use additional information from the desktop
1326 * Note that for thread safety reasons, this function can only
1330 egg_set_desktop_file (const char *desktop_file_path)
1332 GError *error = NULL;
1334 G_LOCK (egg_desktop_file);
1335 if (egg_desktop_file)
1336 egg_desktop_file_free (egg_desktop_file);
1338 egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
1341 g_warning ("Could not load desktop file '%s': %s",
1342 desktop_file_path, error->message);
1343 g_error_free (error);
1346 /* Set localized application name and default window icon */
1347 if (egg_desktop_file->name)
1348 g_set_application_name (egg_desktop_file->name);
1349 if (egg_desktop_file->icon)
1351 if (g_path_is_absolute (egg_desktop_file->icon))
1352 gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
1354 gtk_window_set_default_icon_name (egg_desktop_file->icon);
1357 G_UNLOCK (egg_desktop_file);
1361 * egg_get_desktop_file:
1363 * Gets the application's #EggDesktopFile, as set by
1364 * egg_set_desktop_file().
1366 * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
1369 egg_get_desktop_file (void)
1371 EggDesktopFile *retval;
1373 G_LOCK (egg_desktop_file);
1374 retval = egg_desktop_file;
1375 G_UNLOCK (egg_desktop_file);