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);
208
/* See if it takes paths or URIs or neither */
209
for (p = exec; *p; p++)
213
if (p[1] == '\0' || strchr ("FfUu", p[1]))
215
desktop_file->document_code = p[1];
224
else if (!strcmp (type, "Link"))
228
desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
230
url = g_key_file_get_string (key_file,
231
EGG_DESKTOP_FILE_GROUP,
232
EGG_DESKTOP_FILE_KEY_URL,
236
egg_desktop_file_free (desktop_file);
241
else if (!strcmp (type, "Directory"))
242
desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
244
desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
246
/* Check the Icon key */
247
desktop_file->icon = g_key_file_get_string (key_file,
248
EGG_DESKTOP_FILE_GROUP,
249
EGG_DESKTOP_FILE_KEY_ICON,
251
if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
255
/* Lots of .desktop files still get this wrong */
256
ext = strrchr (desktop_file->icon, '.');
257
if (ext && (!strcmp (ext, ".png") ||
258
!strcmp (ext, ".xpm") ||
259
!strcmp (ext, ".svg")))
261
g_warning ("Desktop file '%s' has malformed Icon key '%s'"
262
"(should not include extension)",
263
source ? source : "(unknown)",
269
desktop_file->key_file = key_file;
274
* egg_desktop_file_copy:
275
* @desktop_file: an #EggDesktopFile
277
* Creates a duplicate of @desktop_file.
278
* Return value: a newly allocated #EggDesktopFile
281
egg_desktop_file_copy (const EggDesktopFile *desktop_file)
283
return g_memdup ((gconstpointer)desktop_file, sizeof (EggDesktopFile));
287
* egg_desktop_file_free:
288
* @desktop_file: an #EggDesktopFile
290
* Frees @desktop_file.
293
egg_desktop_file_free (EggDesktopFile *desktop_file)
295
g_key_file_free (desktop_file->key_file);
296
g_free (desktop_file->source);
297
g_free (desktop_file->name);
298
g_free (desktop_file->icon);
299
g_free (desktop_file);
303
* egg_desktop_file_get_key_file:
304
* @desktop_file: an #EggDesktopFile
306
* Gets the #GKeyFile associated with @desktop_file. You must not free
307
* this value, and changes made to it will not be reflected by
310
* Return value: the #GKeyFile associated with @desktop_file.
313
egg_desktop_file_get_key_file (EggDesktopFile *desktop_file)
315
return desktop_file->key_file;
319
* egg_desktop_file_get_source:
320
* @desktop_file: an #EggDesktopFile
322
* Gets the URI that @desktop_file was loaded from.
324
* Return value: @desktop_file's source URI
327
egg_desktop_file_get_source (EggDesktopFile *desktop_file)
329
return desktop_file->source;
333
* egg_desktop_file_get_desktop_file_type:
334
* @desktop_file: an #EggDesktopFile
336
* Gets the desktop file type of @desktop_file.
338
* Return value: @desktop_file's type
341
egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
343
return desktop_file->type;
347
* egg_desktop_file_get_name:
348
* @desktop_file: an #EggDesktopFile
350
* Gets the (localized) value of @desktop_file's "Name" key.
352
* Return value: the application/link name
355
egg_desktop_file_get_name (EggDesktopFile *desktop_file)
357
return desktop_file->name;
361
* egg_desktop_file_get_icon:
362
* @desktop_file: an #EggDesktopFile
364
* Gets the value of @desktop_file's "Icon" key.
366
* If the icon string is a full path (that is, if g_path_is_absolute()
367
* returns %TRUE when called on it), it points to a file containing an
368
* unthemed icon. If the icon string is not a full path, it is the
369
* name of a themed icon, which can be looked up with %GtkIconTheme,
370
* or passed directly to a theme-aware widget like %GtkImage or
371
* %GtkCellRendererPixbuf.
373
* Return value: the icon path or name
376
egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
378
return desktop_file->icon;
382
* egg_desktop_file_can_launch:
383
* @desktop_file: an #EggDesktopFile
384
* @desktop_environment: the name of the running desktop environment,
387
* Tests if @desktop_file can/should be launched in the current
388
* environment. If @desktop_environment is non-%NULL, @desktop_file's
389
* "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
390
* this desktop_file is appropriate for the named environment.
392
* Furthermore, if @desktop_file has type
393
* %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
394
* also checked, to make sure the binary it points to exists.
396
* egg_desktop_file_can_launch() does NOT check the value of the
399
* Return value: %TRUE if @desktop_file can be launched
402
egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
403
const char *desktop_environment)
405
char *try_exec, *found_program;
406
char **only_show_in, **not_show_in;
410
if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
411
desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
414
if (desktop_environment)
416
only_show_in = g_key_file_get_string_list (desktop_file->key_file,
417
EGG_DESKTOP_FILE_GROUP,
418
EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
422
for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
424
if (!strcmp (only_show_in[i], desktop_environment))
428
g_strfreev (only_show_in);
434
not_show_in = g_key_file_get_string_list (desktop_file->key_file,
435
EGG_DESKTOP_FILE_GROUP,
436
EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
440
for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
442
if (!strcmp (not_show_in[i], desktop_environment))
446
g_strfreev (not_show_in);
453
if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
455
try_exec = g_key_file_get_string (desktop_file->key_file,
456
EGG_DESKTOP_FILE_GROUP,
457
EGG_DESKTOP_FILE_KEY_TRY_EXEC,
461
found_program = g_find_program_in_path (try_exec);
466
g_free (found_program);
474
* egg_desktop_file_accepts_documents:
475
* @desktop_file: an #EggDesktopFile
477
* Tests if @desktop_file represents an application that can accept
478
* documents on the command line.
480
* Return value: %TRUE or %FALSE
483
egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
485
return desktop_file->document_code != 0;
489
* egg_desktop_file_accepts_multiple:
490
* @desktop_file: an #EggDesktopFile
492
* Tests if @desktop_file can accept multiple documents at once.
494
* If this returns %FALSE, you can still pass multiple documents to
495
* egg_desktop_file_launch(), but that will result in multiple copies
496
* of the application being launched. See egg_desktop_file_launch()
499
* Return value: %TRUE or %FALSE
502
egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
504
return (desktop_file->document_code == 'F' ||
505
desktop_file->document_code == 'U');
509
* egg_desktop_file_accepts_uris:
510
* @desktop_file: an #EggDesktopFile
512
* Tests if @desktop_file can accept (non-"file:") URIs as documents to
515
* Return value: %TRUE or %FALSE
518
egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
520
return (desktop_file->document_code == 'U' ||
521
desktop_file->document_code == 'u');
525
append_quoted_word (GString *str,
527
gboolean in_single_quotes,
528
gboolean in_double_quotes)
532
if (!in_single_quotes && !in_double_quotes)
533
g_string_append_c (str, '\'');
534
else if (!in_single_quotes && in_double_quotes)
535
g_string_append (str, "\"'");
537
if (!strchr (s, '\''))
538
g_string_append (str, s);
541
for (p = s; *p != '\0'; p++)
544
g_string_append (str, "'\\''");
546
g_string_append_c (str, *p);
550
if (!in_single_quotes && !in_double_quotes)
551
g_string_append_c (str, '\'');
552
else if (!in_single_quotes && in_double_quotes)
553
g_string_append (str, "'\"");
557
do_percent_subst (EggDesktopFile *desktop_file,
561
gboolean in_single_quotes,
562
gboolean in_double_quotes)
570
g_string_append_c (str, '%');
575
for (d = *documents; d; d = d->next)
578
g_string_append (str, " ");
579
append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
588
doc = (*documents)->data;
589
g_string_append (str, " ");
590
append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
591
*documents = (*documents)->next;
596
if (desktop_file->icon)
598
g_string_append (str, "--icon ");
599
append_quoted_word (str, desktop_file->icon,
600
in_single_quotes, in_double_quotes);
605
if (desktop_file->name)
607
append_quoted_word (str, desktop_file->name,
608
in_single_quotes, in_double_quotes);
613
if (desktop_file->source)
615
append_quoted_word (str, desktop_file->source,
616
in_single_quotes, in_double_quotes);
626
/* Deprecated; skip */
630
g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
636
parse_exec (EggDesktopFile *desktop_file,
640
char *exec, *p, *command;
641
gboolean escape, single_quot, double_quot;
644
exec = g_key_file_get_string (desktop_file->key_file,
645
EGG_DESKTOP_FILE_GROUP,
646
EGG_DESKTOP_FILE_KEY_EXEC,
651
/* Build the command */
652
gs = g_string_new (NULL);
653
escape = single_quot = double_quot = FALSE;
655
for (p = exec; *p != '\0'; p++)
660
g_string_append_c (gs, *p);
666
g_string_append_c (gs, *p);
670
g_string_append_c (gs, *p);
671
if (!single_quot && !double_quot)
673
else if (single_quot)
678
g_string_append_c (gs, *p);
679
if (!single_quot && !double_quot)
681
else if (double_quot)
684
else if (*p == '%' && p[1])
686
do_percent_subst (desktop_file, p[1], gs, documents,
687
single_quot, double_quot);
691
g_string_append_c (gs, *p);
695
command = g_string_free (gs, FALSE);
697
/* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
698
if (g_key_file_has_key (desktop_file->key_file,
699
EGG_DESKTOP_FILE_GROUP,
700
EGG_DESKTOP_FILE_KEY_TERMINAL,
703
GError *terminal_error = NULL;
704
gboolean use_terminal =
705
g_key_file_get_boolean (desktop_file->key_file,
706
EGG_DESKTOP_FILE_GROUP,
707
EGG_DESKTOP_FILE_KEY_TERMINAL,
712
g_propagate_error (error, terminal_error);
718
gs = g_string_new ("xdg-terminal ");
719
append_quoted_word (gs, command, FALSE, FALSE);
721
command = g_string_free (gs, FALSE);
729
translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
731
gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
734
for (d = documents, ret = NULL; d; d = d->next)
736
const char *document = d->data;
737
gboolean is_uri = !g_path_is_absolute (document);
743
translated = g_strdup (document);
745
translated = g_filename_to_uri (document, NULL, NULL);
750
translated = g_filename_from_uri (document, NULL, NULL);
752
translated = g_strdup (document);
756
ret = g_slist_prepend (ret, translated);
759
return g_slist_reverse (ret);
763
free_document_list (GSList *documents)
767
for (d = documents; d; d = d->next)
769
g_slist_free (documents);
773
* egg_desktop_file_parse_exec:
774
* @desktop_file: a #EggDesktopFile
775
* @documents: a list of document paths or URIs
776
* @error: error pointer
778
* Parses @desktop_file's Exec key, inserting @documents into it, and
779
* returns the result.
781
* If @documents contains non-file: URIs and @desktop_file does not
782
* accept URIs, those URIs will be ignored. Likewise, if @documents
783
* contains more elements than @desktop_file accepts, the extra
784
* documents will be ignored.
786
* Return value: the parsed Exec string
789
egg_desktop_file_parse_exec (EggDesktopFile *desktop_file,
793
GSList *translated, *docs;
796
docs = translated = translate_document_list (desktop_file, documents);
797
command = parse_exec (desktop_file, &docs, error);
798
free_document_list (translated);
804
parse_link (EggDesktopFile *desktop_file,
805
EggDesktopFile **app_desktop_file,
812
url = g_key_file_get_string (desktop_file->key_file,
813
EGG_DESKTOP_FILE_GROUP,
814
EGG_DESKTOP_FILE_KEY_URL,
818
*documents = g_slist_prepend (NULL, url);
820
/* FIXME: use gvfs */
821
key_file = g_key_file_new ();
822
g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
823
EGG_DESKTOP_FILE_KEY_NAME,
825
g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
826
EGG_DESKTOP_FILE_KEY_TYPE,
828
g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
829
EGG_DESKTOP_FILE_KEY_EXEC,
831
*app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
835
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
837
start_startup_notification (GdkDisplay *display,
838
EggDesktopFile *desktop_file,
844
static int sequence = 0;
846
char *description, *wmclass;
847
char *screen_str, *workspace_str;
849
if (g_key_file_has_key (desktop_file->key_file,
850
EGG_DESKTOP_FILE_GROUP,
851
EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
854
if (!g_key_file_get_boolean (desktop_file->key_file,
855
EGG_DESKTOP_FILE_GROUP,
856
EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
863
wmclass = g_key_file_get_string (desktop_file->key_file,
864
EGG_DESKTOP_FILE_GROUP,
865
EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
871
if (launch_time == (guint32)-1)
872
launch_time = gdk_x11_display_get_user_time (display);
873
startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
875
(unsigned long)getpid (),
879
(unsigned long)launch_time);
881
description = g_strdup_printf (_("Starting %s"), desktop_file->name);
882
screen_str = g_strdup_printf ("%d", screen);
883
workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
885
gdk_x11_display_broadcast_startup_message (display, "new",
887
"NAME", desktop_file->name,
888
"SCREEN", screen_str,
890
"ICON", desktop_file->icon,
891
"DESKTOP", workspace_str,
892
"DESCRIPTION", description,
896
g_free (description);
899
g_free (workspace_str);
905
end_startup_notification (GdkDisplay *display,
906
const char *startup_id)
908
gdk_x11_display_broadcast_startup_message (display, "remove",
913
#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
918
} StartupNotificationData;
921
startup_notification_timeout (gpointer data)
923
StartupNotificationData *sn_data = data;
925
end_startup_notification (sn_data->display, sn_data->startup_id);
926
g_object_unref (sn_data->display);
927
g_free (sn_data->startup_id);
934
set_startup_notification_timeout (GdkDisplay *display,
935
const char *startup_id)
937
StartupNotificationData *sn_data;
939
sn_data = g_new (StartupNotificationData, 1);
940
sn_data->display = g_object_ref (display);
941
sn_data->startup_id = g_strdup (startup_id);
943
g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
944
startup_notification_timeout, sn_data);
946
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
948
extern char **environ;
951
array_putenv (GPtrArray *env, char *variable)
957
env = g_ptr_array_new ();
959
for (i = 0; environ[i]; i++)
960
g_ptr_array_add (env, g_strdup (environ[i]));
963
keylen = strcspn (variable, "=");
965
/* Remove old value of key */
966
for (i = 0; i < env->len; i++)
968
char *envvar = env->pdata[i];
970
if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
973
g_ptr_array_remove_index_fast (env, i);
979
g_ptr_array_add (env, g_strdup (variable));
985
egg_desktop_file_launchv (EggDesktopFile *desktop_file,
986
GSList *documents, va_list args,
989
EggDesktopFileLaunchOption option;
990
GSList *translated_documents, *docs;
991
char *command, **argv;
992
int argc, i, screen_num;
993
gboolean success, current_success;
997
GPtrArray *env = NULL;
998
char **variables = NULL;
999
GdkScreen *screen = NULL;
1001
const char *directory = NULL;
1002
guint32 launch_time = (guint32)-1;
1003
GSpawnFlags flags = G_SPAWN_SEARCH_PATH;
1004
GSpawnChildSetupFunc setup_func = NULL;
1005
gpointer setup_data = NULL;
1007
GPid *ret_pid = NULL;
1008
int *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
1009
char **ret_startup_id = NULL;
1011
if (documents && desktop_file->document_code == 0)
1013
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1014
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1015
_("Application does not accept documents on command line"));
1019
/* Read the options: technically it's incorrect for the caller to
1020
* NULL-terminate the list of options (rather than 0-terminating
1021
* it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
1022
* it's more consistent with other glib/gtk methods, and it will
1023
* work as long as sizeof (int) <= sizeof (NULL), and NULL is
1024
* represented as 0. (Which is true everywhere we care about.)
1026
while ((option = va_arg (args, EggDesktopFileLaunchOption)))
1030
case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
1032
g_ptr_array_free (env, TRUE);
1033
env = g_ptr_array_new ();
1035
case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
1036
variables = va_arg (args, char **);
1037
for (i = 0; variables[i]; i++)
1038
env = array_putenv (env, variables[i]);
1041
case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
1042
screen = va_arg (args, GdkScreen *);
1044
case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
1045
workspace = va_arg (args, int);
1048
case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
1049
directory = va_arg (args, const char *);
1051
case EGG_DESKTOP_FILE_LAUNCH_TIME:
1052
launch_time = va_arg (args, guint32);
1054
case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
1055
flags |= va_arg (args, GSpawnFlags);
1056
/* Make sure they didn't set any flags that don't make sense. */
1057
flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
1059
case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
1060
setup_func = va_arg (args, GSpawnChildSetupFunc);
1061
setup_data = va_arg (args, gpointer);
1064
case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
1065
ret_pid = va_arg (args, GPid *);
1067
case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
1068
ret_stdin = va_arg (args, int *);
1070
case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
1071
ret_stdout = va_arg (args, int *);
1073
case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
1074
ret_stderr = va_arg (args, int *);
1076
case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
1077
ret_startup_id = va_arg (args, char **);
1081
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1082
EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
1083
_("Unrecognized launch option: %d"),
1084
GPOINTER_TO_INT (option));
1092
char *display_name = gdk_screen_make_display_name (screen);
1093
char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
1094
env = array_putenv (env, display_env);
1095
g_free (display_name);
1096
g_free (display_env);
1098
display = gdk_screen_get_display (screen);
1102
display = gdk_display_get_default ();
1103
screen = gdk_display_get_default_screen (display);
1105
screen_num = gdk_screen_get_number (screen);
1107
translated_documents = translate_document_list (desktop_file, documents);
1108
docs = translated_documents;
1114
command = parse_exec (desktop_file, &docs, error);
1118
if (!g_shell_parse_argv (command, &argc, &argv, error))
1125
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
1126
startup_id = start_startup_notification (display, desktop_file,
1127
argv[0], screen_num,
1128
workspace, launch_time);
1131
char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
1133
env = array_putenv (env, startup_id_env);
1134
g_free (startup_id_env);
1138
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
1141
g_spawn_async_with_pipes (directory,
1143
env ? (char **)(env->pdata) : NULL,
1145
setup_func, setup_data,
1147
ret_stdin, ret_stdout, ret_stderr,
1153
#ifdef HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE
1154
if (current_success)
1156
set_startup_notification_timeout (display, startup_id);
1159
*ret_startup_id = startup_id;
1161
g_free (startup_id);
1164
#endif /* HAVE_GDK_X11_DISPLAY_BROADCAST_STARTUP_MESSAGE */
1165
g_free (startup_id);
1167
else if (ret_startup_id)
1168
*ret_startup_id = NULL;
1170
if (current_success)
1172
/* If we successfully launch any instances of the app, make
1173
* sure we return TRUE and don't set @error.
1178
/* Also, only set the output params on the first one */
1180
ret_stdin = ret_stdout = ret_stderr = NULL;
1181
ret_startup_id = NULL;
1184
while (docs && current_success);
1189
/* this raises a segfault
1190
g_strfreev ((char **)env->pdata);
1192
g_ptr_array_free (env, FALSE);
1194
free_document_list (translated_documents);
1200
* egg_desktop_file_launch:
1201
* @desktop_file: an #EggDesktopFile
1202
* @documents: a list of URIs or paths to documents to open
1203
* @error: error pointer
1204
* @...: additional options
1206
* Launches @desktop_file with the given arguments. Additional options
1207
* can be specified as follows:
1209
* %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
1210
* clears the environment in the child process
1211
* %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
1212
* adds the NAME=VALUE strings in the given %NULL-terminated
1213
* array to the child process's environment
1214
* %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
1215
* causes the application to be launched on the given screen
1216
* %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
1217
* causes the application to be launched on the given workspace
1218
* %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
1219
* causes the application to be launched in the given directory
1220
* %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
1221
* sets the "launch time" for the application. If the user
1222
* interacts with another window after @launch_time but before
1223
* the launched application creates its first window, the window
1224
* manager may choose to not give focus to the new application.
1225
* Passing 0 for @launch_time will explicitly request that the
1226
* application not receive focus.
1227
* %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
1228
* Sets additional #GSpawnFlags to use. See g_spawn_async() for
1230
* %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
1231
* Sets the child setup callback and the data to pass to it.
1232
* (See g_spawn_async() for more details.)
1234
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
1235
* On a successful launch, sets *@pid to the PID of the launched
1237
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
1238
* On a successful launch, sets *@startup_id to the Startup
1239
* Notification "startup id" of the launched application.
1240
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
1241
* On a successful launch, sets *@fd to the file descriptor of
1242
* a pipe connected to the application's stdin.
1243
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
1244
* On a successful launch, sets *@fd to the file descriptor of
1245
* a pipe connected to the application's stdout.
1246
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
1247
* On a successful launch, sets *@fd to the file descriptor of
1248
* a pipe connected to the application's stderr.
1250
* The options should be terminated with a single %NULL.
1252
* If @documents contains multiple documents, but
1253
* egg_desktop_file_accepts_multiple() returns %FALSE for
1254
* @desktop_file, then egg_desktop_file_launch() will actually launch
1255
* multiple instances of the application. In that case, the return
1256
* value (as well as any values passed via
1257
* %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
1258
* first instance of the application that was launched (but the
1259
* %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
1262
* Return value: %TRUE if the application was successfully launched.
1265
egg_desktop_file_launch (EggDesktopFile *desktop_file,
1266
GSList *documents, GError **error,
1271
EggDesktopFile *app_desktop_file;
1273
switch (desktop_file->type)
1275
case EGG_DESKTOP_FILE_TYPE_APPLICATION:
1276
va_start (args, error);
1277
success = egg_desktop_file_launchv (desktop_file, documents,
1282
case EGG_DESKTOP_FILE_TYPE_LINK:
1285
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1286
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1287
_("Can't pass document URIs to a 'Type=Link' desktop entry"));
1291
if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
1294
va_start (args, error);
1295
success = egg_desktop_file_launchv (app_desktop_file, documents,
1299
egg_desktop_file_free (app_desktop_file);
1300
free_document_list (documents);
1304
g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1305
EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1306
_("Not a launchable item"));
1316
egg_desktop_file_error_quark (void)
1318
return g_quark_from_static_string ("egg-desktop_file-error-quark");
1322
G_LOCK_DEFINE_STATIC (egg_desktop_file);
1323
static EggDesktopFile *egg_desktop_file;
1326
* egg_set_desktop_file:
1327
* @desktop_file_path: path to the application's desktop file
1329
* Creates an #EggDesktopFile for the application from the data at
1330
* @desktop_file_path. This will also call g_set_application_name()
1331
* with the localized application name from the desktop file, and
1332
* gtk_window_set_default_icon_name() or
1333
* gtk_window_set_default_icon_from_file() with the application's
1334
* icon. Other code may use additional information from the desktop
1337
* Note that for thread safety reasons, this function can only
1341
egg_set_desktop_file (const char *desktop_file_path)
1343
GError *error = NULL;
1345
G_LOCK (egg_desktop_file);
1346
if (egg_desktop_file)
1347
egg_desktop_file_free (egg_desktop_file);
1349
egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
1352
g_warning ("Could not load desktop file '%s': %s",
1353
desktop_file_path, error->message);
1354
g_error_free (error);
1357
/* Set localized application name and default window icon */
1358
if (egg_desktop_file->name)
1359
g_set_application_name (egg_desktop_file->name);
1360
if (egg_desktop_file->icon)
1362
if (g_path_is_absolute (egg_desktop_file->icon))
1363
gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
1365
gtk_window_set_default_icon_name (egg_desktop_file->icon);
1368
G_UNLOCK (egg_desktop_file);
1372
* egg_get_desktop_file:
1374
* Gets the application's #EggDesktopFile, as set by
1375
* egg_set_desktop_file().
1377
* Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
1380
egg_get_desktop_file (void)
1382
EggDesktopFile *retval;
1384
G_LOCK (egg_desktop_file);
1385
retval = egg_desktop_file;
1386
G_UNLOCK (egg_desktop_file);
1392
_egg_desktop_file_copy (gpointer boxed)
1394
return egg_desktop_file_copy (EGG_DESKTOP_FILE (boxed));
1398
_egg_desktop_file_free (gpointer boxed)
1400
egg_desktop_file_free (EGG_DESKTOP_FILE (boxed));
1403
GType egg_desktop_file_get_type (void)
1405
static GType type = 0;
1408
type = g_boxed_type_register_static ("EggDesktopFile",
1409
_egg_desktop_file_copy,
1410
_egg_desktop_file_free);