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 this library. If not, see <http://www.gnu.org/licenses/>.
18 Author: Alexander Larsson <alexl@redhat.com>
23 #include "gdkx11applaunchcontext.h"
24 #include "gdkapplaunchcontextprivate.h"
25 #include "gdkscreen.h"
27 #include "gdkprivate-x11.h"
30 #include <gio/gdesktopappinfo.h>
36 get_display_name (GFile *file,
43 name = g_strdup (g_file_info_get_display_name (info));
47 name = g_file_get_basename (file);
48 if (!g_utf8_validate (name, -1, NULL))
52 g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
61 get_icon (GFile *file,
70 icon = g_file_info_get_icon (info);
79 gicon_to_string (GIcon *icon)
82 const char *const *names;
84 if (G_IS_FILE_ICON (icon))
86 file = g_file_icon_get_file (G_FILE_ICON (icon));
88 return g_file_get_path (file);
90 else if (G_IS_THEMED_ICON (icon))
92 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
94 return g_strdup (names[0]);
96 else if (G_IS_EMBLEMED_ICON (icon))
100 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
102 return gicon_to_string (base);
109 end_startup_notification (GdkDisplay *display,
110 const char *startup_id)
112 gdk_x11_display_broadcast_startup_message (display, "remove",
118 /* This should be fairly long, as it's confusing to users if a startup
119 * ends when it shouldn't (it appears that the startup failed, and
120 * they have to relaunch the app). Also the timeout only matters when
121 * there are bugs and apps don't end their own startup sequence.
123 * This timeout is a "last resort" timeout that ignores whether the
124 * startup sequence has shown activity or not. Metacity and the
125 * tasklist have smarter, and correspondingly able-to-be-shorter
126 * timeouts. The reason our timeout is dumb is that we don't monitor
127 * the sequence (don't use an SnMonitorContext)
129 #define STARTUP_TIMEOUT_LENGTH_SECONDS 30
130 #define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
137 } StartupNotificationData;
140 free_startup_notification_data (gpointer data)
142 StartupNotificationData *sn_data = data;
144 g_object_unref (sn_data->display);
145 g_free (sn_data->startup_id);
153 } StartupTimeoutData;
156 free_startup_timeout (void *data)
158 StartupTimeoutData *std;
162 g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
163 g_slist_free (std->contexts);
165 if (std->timeout_id != 0)
167 g_source_remove (std->timeout_id);
175 startup_timeout (void *data)
177 StartupTimeoutData *std;
184 min_timeout = STARTUP_TIMEOUT_LENGTH;
186 g_get_current_time (&now);
191 StartupNotificationData *sn_data;
199 ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
200 (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
202 if (elapsed >= STARTUP_TIMEOUT_LENGTH)
204 std->contexts = g_slist_remove (std->contexts, sn_data);
205 end_startup_notification (sn_data->display, sn_data->startup_id);
206 free_startup_notification_data (sn_data);
210 min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
216 if (std->contexts == NULL)
219 std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
221 /* always remove this one, but we may have reinstalled another one. */
222 return G_SOURCE_REMOVE;
227 add_startup_timeout (GdkScreen *screen,
228 const char *startup_id)
230 StartupTimeoutData *data;
231 StartupNotificationData *sn_data;
233 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
237 data = g_new (StartupTimeoutData, 1);
238 data->contexts = NULL;
239 data->timeout_id = 0;
241 g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
242 data, free_startup_timeout);
245 sn_data = g_new (StartupNotificationData, 1);
246 sn_data->display = g_object_ref (gdk_screen_get_display (screen));
247 sn_data->startup_id = g_strdup (startup_id);
248 g_get_current_time (&sn_data->time);
250 data->contexts = g_slist_prepend (data->contexts, sn_data);
252 if (data->timeout_id == 0)
253 data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
254 startup_timeout, data);
259 gdk_x11_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
263 static int sequence = 0;
269 const char *binary_name;
270 const char *application_id;
277 GdkAppLaunchContext *ctx;
279 ctx = GDK_APP_LAUNCH_CONTEXT (context);
281 display = ctx->display;
283 screen = ctx->screen;
285 screen = gdk_display_get_default_screen (ctx->display);
289 files_count = g_list_length (files);
290 if (files_count == 0)
292 description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
294 else if (files_count == 1)
298 if (g_file_is_native (files->data))
299 fileinfo = g_file_query_info (files->data,
300 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
301 G_FILE_ATTRIBUTE_STANDARD_ICON,
304 display_name = get_display_name (files->data, fileinfo);
305 description = g_strdup_printf (_("Opening %s"), display_name);
306 g_free (display_name);
309 description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
312 files_count), files_count);
316 icon_name = g_strdup (ctx->icon_name);
321 if (ctx->icon != NULL)
322 icon = g_object_ref (ctx->icon);
323 else if (files_count == 1)
324 icon = get_icon (files->data, fileinfo);
328 icon = g_app_info_get_icon (info);
335 icon_name = gicon_to_string (icon);
336 g_object_unref (icon);
340 binary_name = g_app_info_get_executable (info);
342 timestamp = ctx->timestamp;
343 if (timestamp == GDK_CURRENT_TIME)
344 timestamp = gdk_x11_display_get_user_time (display);
346 screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
347 if (ctx->workspace > -1)
348 workspace_str = g_strdup_printf ("%d", ctx->workspace);
350 workspace_str = NULL;
352 if (G_IS_DESKTOP_APP_INFO (info))
353 application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
355 application_id = NULL;
357 startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
359 (unsigned long)getpid (),
363 (unsigned long)timestamp);
365 gdk_x11_display_broadcast_startup_message (display, "new",
367 "NAME", g_app_info_get_name (info),
368 "SCREEN", screen_str,
371 "DESKTOP", workspace_str,
372 "DESCRIPTION", description,
373 "WMCLASS", NULL, /* FIXME */
374 "APPLICATION_ID", application_id,
377 g_free (description);
379 g_free (workspace_str);
382 g_object_unref (fileinfo);
384 add_startup_timeout (screen, startup_id);
391 gdk_x11_app_launch_context_launch_failed (GAppLaunchContext *context,
392 const gchar *startup_notify_id)
394 GdkAppLaunchContext *ctx;
396 StartupTimeoutData *data;
397 StartupNotificationData *sn_data;
400 ctx = GDK_APP_LAUNCH_CONTEXT (context);
403 screen = ctx->screen;
405 screen = gdk_display_get_default_screen (ctx->display);
407 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
411 for (l = data->contexts; l != NULL; l = l->next)
414 if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
416 data->contexts = g_slist_remove (data->contexts, sn_data);
417 end_startup_notification (sn_data->display, sn_data->startup_id);
418 free_startup_notification_data (sn_data);
424 if (data->contexts == NULL)
426 g_source_remove (data->timeout_id);
427 data->timeout_id = 0;
432 struct _GdkX11AppLaunchContext
434 GdkAppLaunchContext parent_instance;
437 struct _GdkX11AppLaunchContextClass
439 GdkAppLaunchContextClass parent_class;
443 G_DEFINE_TYPE (GdkX11AppLaunchContext, gdk_x11_app_launch_context, GDK_TYPE_APP_LAUNCH_CONTEXT)
446 gdk_x11_app_launch_context_class_init (GdkX11AppLaunchContextClass *klass)
448 GAppLaunchContextClass *ctx_class = G_APP_LAUNCH_CONTEXT_CLASS (klass);
450 ctx_class->get_startup_notify_id = gdk_x11_app_launch_context_get_startup_notify_id;
451 ctx_class->launch_failed = gdk_x11_app_launch_context_launch_failed;
455 gdk_x11_app_launch_context_init (GdkX11AppLaunchContext *ctx)
459 GdkAppLaunchContext *
460 _gdk_x11_display_get_app_launch_context (GdkDisplay *display)
462 GdkAppLaunchContext *ctx;
464 ctx = g_object_new (GDK_TYPE_X11_APP_LAUNCH_CONTEXT,