1 /* gdkapplaunchcontext-x11.c - Gtk+ implementation for GAppLaunchContext
3 Copyright (C) 2007 Red Hat, Inc.
5 The Gnome Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The Gnome Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the Gnome Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
20 Author: Alexander Larsson <alexl@redhat.com>
25 #include "gdkapplaunchcontext.h"
28 #include "gdkscreen.h"
29 #include "gdkinternals.h"
33 #include <gio/gdesktopappinfo.h>
39 get_display_name (GFile *file,
46 name = g_strdup (g_file_info_get_display_name (info));
50 name = g_file_get_basename (file);
51 if (!g_utf8_validate (name, -1, NULL))
55 g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
64 get_icon (GFile *file,
73 icon = g_file_info_get_icon (info);
82 gicon_to_string (GIcon *icon)
85 const char *const *names;
87 if (G_IS_FILE_ICON (icon))
89 file = g_file_icon_get_file (G_FILE_ICON (icon));
91 return g_file_get_path (file);
93 else if (G_IS_THEMED_ICON (icon))
95 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
97 return g_strdup (names[0]);
99 else if (G_IS_EMBLEMED_ICON (icon))
103 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
105 return gicon_to_string (base);
112 end_startup_notification (GdkDisplay *display,
113 const char *startup_id)
115 gdk_x11_display_broadcast_startup_message (display, "remove",
121 /* This should be fairly long, as it's confusing to users if a startup
122 * ends when it shouldn't (it appears that the startup failed, and
123 * they have to relaunch the app). Also the timeout only matters when
124 * there are bugs and apps don't end their own startup sequence.
126 * This timeout is a "last resort" timeout that ignores whether the
127 * startup sequence has shown activity or not. Metacity and the
128 * tasklist have smarter, and correspondingly able-to-be-shorter
129 * timeouts. The reason our timeout is dumb is that we don't monitor
130 * the sequence (don't use an SnMonitorContext)
132 #define STARTUP_TIMEOUT_LENGTH_SECONDS 30
133 #define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
140 } StartupNotificationData;
143 free_startup_notification_data (gpointer data)
145 StartupNotificationData *sn_data = data;
147 g_object_unref (sn_data->display);
148 g_free (sn_data->startup_id);
156 } StartupTimeoutData;
159 free_startup_timeout (void *data)
161 StartupTimeoutData *std;
165 g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
166 g_slist_free (std->contexts);
168 if (std->timeout_id != 0)
170 g_source_remove (std->timeout_id);
178 startup_timeout (void *data)
180 StartupTimeoutData *std;
187 min_timeout = STARTUP_TIMEOUT_LENGTH;
189 g_get_current_time (&now);
194 StartupNotificationData *sn_data;
202 ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
203 (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
205 if (elapsed >= STARTUP_TIMEOUT_LENGTH)
207 std->contexts = g_slist_remove (std->contexts, sn_data);
208 end_startup_notification (sn_data->display, sn_data->startup_id);
209 free_startup_notification_data (sn_data);
213 min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
219 if (std->contexts == NULL)
222 std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
224 /* always remove this one, but we may have reinstalled another one. */
230 add_startup_timeout (GdkScreen *screen,
231 const char *startup_id)
233 StartupTimeoutData *data;
234 StartupNotificationData *sn_data;
236 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
240 data = g_new (StartupTimeoutData, 1);
241 data->contexts = NULL;
242 data->timeout_id = 0;
244 g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
245 data, free_startup_timeout);
248 sn_data = g_new (StartupNotificationData, 1);
249 sn_data->display = g_object_ref (gdk_screen_get_display (screen));
250 sn_data->startup_id = g_strdup (startup_id);
251 g_get_current_time (&sn_data->time);
253 data->contexts = g_slist_prepend (data->contexts, sn_data);
255 if (data->timeout_id == 0)
256 data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
257 startup_timeout, data);
262 _gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
266 static int sequence = 0;
267 GdkAppLaunchContextPrivate *priv;
273 const char *binary_name;
274 const char *application_id;
282 priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
286 screen = priv->screen;
287 display = gdk_screen_get_display (priv->screen);
289 else if (priv->display)
291 display = priv->display;
292 screen = gdk_display_get_default_screen (display);
296 display = gdk_display_get_default ();
297 screen = gdk_display_get_default_screen (display);
302 files_count = g_list_length (files);
303 if (files_count == 0)
305 description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
307 else if (files_count == 1)
311 if (g_file_is_native (files->data))
312 fileinfo = g_file_query_info (files->data,
313 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
314 G_FILE_ATTRIBUTE_STANDARD_ICON,
317 display_name = get_display_name (files->data, fileinfo);
318 description = g_strdup_printf (_("Opening %s"), display_name);
319 g_free (display_name);
322 description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
325 files_count), files_count);
329 icon_name = g_strdup (priv->icon_name);
334 if (priv->icon != NULL)
335 icon = g_object_ref (priv->icon);
336 else if (files_count == 1)
337 icon = get_icon (files->data, fileinfo);
341 icon = g_app_info_get_icon (info);
346 icon_name = gicon_to_string (icon);
348 g_object_unref (icon);
351 binary_name = g_app_info_get_executable (info);
353 timestamp = priv->timestamp;
354 if (timestamp == GDK_CURRENT_TIME)
355 timestamp = gdk_x11_display_get_user_time (display);
357 screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
358 if (priv->workspace > -1)
359 workspace_str = g_strdup_printf ("%d", priv->workspace);
361 workspace_str = NULL;
363 if (G_IS_DESKTOP_APP_INFO (info))
364 application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
366 application_id = NULL;
368 startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
370 (unsigned long)getpid (),
374 (unsigned long)timestamp);
376 gdk_x11_display_broadcast_startup_message (display, "new",
378 "NAME", g_app_info_get_name (info),
379 "SCREEN", screen_str,
382 "DESKTOP", workspace_str,
383 "DESCRIPTION", description,
384 "WMCLASS", NULL, /* FIXME */
385 "APPLICATION_ID", application_id,
388 g_free (description);
390 g_free (workspace_str);
393 g_object_unref (fileinfo);
395 add_startup_timeout (screen, startup_id);
402 _gdk_windowing_launch_failed (GAppLaunchContext *context,
403 const char *startup_notify_id)
405 GdkAppLaunchContextPrivate *priv;
407 StartupTimeoutData *data;
408 StartupNotificationData *sn_data;
411 priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
414 screen = priv->screen;
415 else if (priv->display)
416 screen = gdk_display_get_default_screen (priv->display);
418 screen = gdk_display_get_default_screen (gdk_display_get_default ());
420 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
424 for (l = data->contexts; l != NULL; l = l->next)
427 if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
429 data->contexts = g_slist_remove (data->contexts, sn_data);
430 end_startup_notification (sn_data->display, sn_data->startup_id);
431 free_startup_notification_data (sn_data);
437 if (data->contexts == NULL)
439 g_source_remove (data->timeout_id);
440 data->timeout_id = 0;