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 "gdkx11applaunchcontext.h"
26 #include "gdkapplaunchcontextprivate.h"
27 #include "gdkscreen.h"
29 #include "gdkprivate-x11.h"
32 #include <gio/gdesktopappinfo.h>
38 get_display_name (GFile *file,
45 name = g_strdup (g_file_info_get_display_name (info));
49 name = g_file_get_basename (file);
50 if (!g_utf8_validate (name, -1, NULL))
54 g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
63 get_icon (GFile *file,
72 icon = g_file_info_get_icon (info);
81 gicon_to_string (GIcon *icon)
84 const char *const *names;
86 if (G_IS_FILE_ICON (icon))
88 file = g_file_icon_get_file (G_FILE_ICON (icon));
90 return g_file_get_path (file);
92 else if (G_IS_THEMED_ICON (icon))
94 names = g_themed_icon_get_names (G_THEMED_ICON (icon));
96 return g_strdup (names[0]);
98 else if (G_IS_EMBLEMED_ICON (icon))
102 base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
104 return gicon_to_string (base);
111 end_startup_notification (GdkDisplay *display,
112 const char *startup_id)
114 gdk_x11_display_broadcast_startup_message (display, "remove",
120 /* This should be fairly long, as it's confusing to users if a startup
121 * ends when it shouldn't (it appears that the startup failed, and
122 * they have to relaunch the app). Also the timeout only matters when
123 * there are bugs and apps don't end their own startup sequence.
125 * This timeout is a "last resort" timeout that ignores whether the
126 * startup sequence has shown activity or not. Metacity and the
127 * tasklist have smarter, and correspondingly able-to-be-shorter
128 * timeouts. The reason our timeout is dumb is that we don't monitor
129 * the sequence (don't use an SnMonitorContext)
131 #define STARTUP_TIMEOUT_LENGTH_SECONDS 30
132 #define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
139 } StartupNotificationData;
142 free_startup_notification_data (gpointer data)
144 StartupNotificationData *sn_data = data;
146 g_object_unref (sn_data->display);
147 g_free (sn_data->startup_id);
155 } StartupTimeoutData;
158 free_startup_timeout (void *data)
160 StartupTimeoutData *std;
164 g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
165 g_slist_free (std->contexts);
167 if (std->timeout_id != 0)
169 g_source_remove (std->timeout_id);
177 startup_timeout (void *data)
179 StartupTimeoutData *std;
186 min_timeout = STARTUP_TIMEOUT_LENGTH;
188 g_get_current_time (&now);
193 StartupNotificationData *sn_data;
201 ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
202 (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
204 if (elapsed >= STARTUP_TIMEOUT_LENGTH)
206 std->contexts = g_slist_remove (std->contexts, sn_data);
207 end_startup_notification (sn_data->display, sn_data->startup_id);
208 free_startup_notification_data (sn_data);
212 min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
218 if (std->contexts == NULL)
221 std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
223 /* always remove this one, but we may have reinstalled another one. */
224 return G_SOURCE_REMOVE;
229 add_startup_timeout (GdkScreen *screen,
230 const char *startup_id)
232 StartupTimeoutData *data;
233 StartupNotificationData *sn_data;
235 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
239 data = g_new (StartupTimeoutData, 1);
240 data->contexts = NULL;
241 data->timeout_id = 0;
243 g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
244 data, free_startup_timeout);
247 sn_data = g_new (StartupNotificationData, 1);
248 sn_data->display = g_object_ref (gdk_screen_get_display (screen));
249 sn_data->startup_id = g_strdup (startup_id);
250 g_get_current_time (&sn_data->time);
252 data->contexts = g_slist_prepend (data->contexts, sn_data);
254 if (data->timeout_id == 0)
255 data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
256 startup_timeout, data);
261 gdk_x11_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
265 static int sequence = 0;
271 const char *binary_name;
272 const char *application_id;
279 GdkAppLaunchContext *ctx;
281 ctx = GDK_APP_LAUNCH_CONTEXT (context);
283 display = ctx->display;
285 screen = ctx->screen;
287 screen = gdk_display_get_default_screen (ctx->display);
291 files_count = g_list_length (files);
292 if (files_count == 0)
294 description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
296 else if (files_count == 1)
300 if (g_file_is_native (files->data))
301 fileinfo = g_file_query_info (files->data,
302 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
303 G_FILE_ATTRIBUTE_STANDARD_ICON,
306 display_name = get_display_name (files->data, fileinfo);
307 description = g_strdup_printf (_("Opening %s"), display_name);
308 g_free (display_name);
311 description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
314 files_count), files_count);
318 icon_name = g_strdup (ctx->icon_name);
323 if (ctx->icon != NULL)
324 icon = g_object_ref (ctx->icon);
325 else if (files_count == 1)
326 icon = get_icon (files->data, fileinfo);
330 icon = g_app_info_get_icon (info);
337 icon_name = gicon_to_string (icon);
338 g_object_unref (icon);
342 binary_name = g_app_info_get_executable (info);
344 timestamp = ctx->timestamp;
345 if (timestamp == GDK_CURRENT_TIME)
346 timestamp = gdk_x11_display_get_user_time (display);
348 screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
349 if (ctx->workspace > -1)
350 workspace_str = g_strdup_printf ("%d", ctx->workspace);
352 workspace_str = NULL;
354 if (G_IS_DESKTOP_APP_INFO (info))
355 application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
357 application_id = NULL;
359 startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
361 (unsigned long)getpid (),
365 (unsigned long)timestamp);
367 gdk_x11_display_broadcast_startup_message (display, "new",
369 "NAME", g_app_info_get_name (info),
370 "SCREEN", screen_str,
373 "DESKTOP", workspace_str,
374 "DESCRIPTION", description,
375 "WMCLASS", NULL, /* FIXME */
376 "APPLICATION_ID", application_id,
379 g_free (description);
381 g_free (workspace_str);
384 g_object_unref (fileinfo);
386 add_startup_timeout (screen, startup_id);
393 gdk_x11_app_launch_context_launch_failed (GAppLaunchContext *context,
394 const gchar *startup_notify_id)
396 GdkAppLaunchContext *ctx;
398 StartupTimeoutData *data;
399 StartupNotificationData *sn_data;
402 ctx = GDK_APP_LAUNCH_CONTEXT (context);
405 screen = ctx->screen;
407 screen = gdk_display_get_default_screen (ctx->display);
409 data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
413 for (l = data->contexts; l != NULL; l = l->next)
416 if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
418 data->contexts = g_slist_remove (data->contexts, sn_data);
419 end_startup_notification (sn_data->display, sn_data->startup_id);
420 free_startup_notification_data (sn_data);
426 if (data->contexts == NULL)
428 g_source_remove (data->timeout_id);
429 data->timeout_id = 0;
434 struct _GdkX11AppLaunchContext
436 GdkAppLaunchContext parent_instance;
439 struct _GdkX11AppLaunchContextClass
441 GdkAppLaunchContextClass parent_class;
445 G_DEFINE_TYPE (GdkX11AppLaunchContext, gdk_x11_app_launch_context, GDK_TYPE_APP_LAUNCH_CONTEXT)
448 gdk_x11_app_launch_context_class_init (GdkX11AppLaunchContextClass *klass)
450 GAppLaunchContextClass *ctx_class = G_APP_LAUNCH_CONTEXT_CLASS (klass);
452 ctx_class->get_startup_notify_id = gdk_x11_app_launch_context_get_startup_notify_id;
453 ctx_class->launch_failed = gdk_x11_app_launch_context_launch_failed;
457 gdk_x11_app_launch_context_init (GdkX11AppLaunchContext *ctx)
461 GdkAppLaunchContext *
462 _gdk_x11_display_get_app_launch_context (GdkDisplay *display)
464 GdkAppLaunchContext *ctx;
466 ctx = g_object_new (GDK_TYPE_X11_APP_LAUNCH_CONTEXT,