diff --git a/ChangeLog b/ChangeLog index ef1605d244..558433b70a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2007-12-12 Matthias Clasen + + * gdk/gdkapplaunchcontext.[hc]: Implement GAppLaunchContext + in gdk, providing startup notification. (#503203) + + * gdk/x11/gdkapplaunchcontext-x11.c: + * gdk/win32/gdkapplaunchcontext-win32.c: + * gdk/quartz/gdkapplaunchcontext-quartz.c: + * gdk/directfb/gdkapplaunchcontext-directfb.c: Backend-specific + parts. All but X11 are just empty stubs for now. + + * gdk/gdk.symbols: + * gdk/gdkinternals.h: + * gdk/Makefile.am: + * gdk/x11/Makefile.am: + * gdk/win32/Makefile.am: + * gdk/quartz/Makefile.am: + * gdk/directfb/Makefile.am: Necessary glue. + 2007-12-12 Mathias Hasselmann * gtk/gtkdnd.c: Another attempt to improve gtk_drag_dest_set docs. diff --git a/gdk/Makefile.am b/gdk/Makefile.am index aeba1389ce..7baec09371 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -64,6 +64,7 @@ endif # gdk_public_h_sources = \ gdk.h \ + gdkapplaunchcontext.h \ gdkcairo.h \ gdkcolor.h \ gdkcursor.h \ @@ -102,6 +103,7 @@ gdk_headers = \ gdk_c_sources = \ $(medialib_sources) \ gdk.c \ + gdkapplaunchcontext.c \ gdkcairo.c \ gdkcolor.c \ gdkcursor.c \ diff --git a/gdk/directfb/Makefile.am b/gdk/directfb/Makefile.am index 6e5f272968..e7b7966eaf 100644 --- a/gdk/directfb/Makefile.am +++ b/gdk/directfb/Makefile.am @@ -19,6 +19,7 @@ LDADDS = \ noinst_LTLIBRARIES = libgdk-directfb.la libgdk_directfb_la_SOURCES = \ + gdkapplaunchcontext-directfb.c \ gdkcolor-directfb.c \ gdkcursor-directfb.c \ gdkdnd-directfb.c \ diff --git a/gdk/directfb/gdkapplaunchcontext-directfb.c b/gdk/directfb/gdkapplaunchcontext-directfb.c new file mode 100644 index 0000000000..f838ad23e9 --- /dev/null +++ b/gdk/directfb/gdkapplaunchcontext-directfb.c @@ -0,0 +1,42 @@ +/* gdkapplaunchcontext-directfb.c - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Matthias Clasen +*/ + +#include + +#include "gdkapplaunchcontext.h" + + +char * +_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, + GAppInfo *info, + GList *files) +{ + return NULL; +} + +void +_gdk_windowing_launch_failed (GAppLaunchContext *context, + const char *startup_notify_id) +{ +} + + diff --git a/gdk/gdk.symbols b/gdk/gdk.symbols index 1512936339..40f05a2f5e 100644 --- a/gdk/gdk.symbols +++ b/gdk/gdk.symbols @@ -1254,6 +1254,18 @@ gdk_xid_table_lookup_for_display #endif #endif +#if IN_HEADER(__GDK_APP_LAUNCH_CONTEXT_H__) +#if IN_FILE(__GDK_APP_LAUNCH_CONTEXT_C__) +gdk_app_launch_context_new +gdk_app_launch_context_set_display +gdk_app_launch_context_set_screen +gdk_app_launch_context_set_desktop +gdk_app_launch_context_set_timestamp +gdk_app_launch_context_set_icon +gdk_app_launch_context_set_icon_name +#endif +#endif + #ifdef INCLUDE_VARIABLES gdk_threads_mutex gdk_threads_lock diff --git a/gdk/gdkapplaunchcontext.c b/gdk/gdkapplaunchcontext.c new file mode 100644 index 0000000000..d63a2fc524 --- /dev/null +++ b/gdk/gdkapplaunchcontext.c @@ -0,0 +1,186 @@ +/* gdkapplaunchcontext.c - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson +*/ + +#include + +#include +#include + +#include +#include +#include + +#include "gdkapplaunchcontext.h" +#include "gdkinternals.h" +#include "gdkscreen.h" +#include "gdkintl.h" +#include "gdkalias.h" + + +G_DEFINE_TYPE (GdkAppLaunchContext, gdk_app_launch_context, + G_TYPE_APP_LAUNCH_CONTEXT); + +static void +gdk_app_launch_context_finalize (GObject *object) +{ + GdkAppLaunchContext *context; + GdkAppLaunchContextPrivate *priv; + + context = GDK_APP_LAUNCH_CONTEXT (object); + + priv = context->priv; + + if (priv->display) + g_object_unref (priv->display); + + if (priv->screen) + g_object_unref (priv->screen); + + if (priv->icon) + g_object_unref (priv->icon); + + g_free (priv->icon_name); + + (*G_OBJECT_CLASS (gdk_app_launch_context_parent_class)->finalize) (object); +} + +static char * +get_display (GAppLaunchContext *context, + GAppInfo *info, + GList *files) +{ + GdkDisplay *display; + GdkAppLaunchContextPrivate *priv; + + priv = GDK_APP_LAUNCH_CONTEXT (context)->priv; + + if (priv->screen) + return gdk_screen_make_display_name (priv->screen); + + if (priv->display) + display = priv->display; + else + display = gdk_display_get_default (); + + return g_strdup (gdk_display_get_name (display)); +} + +static void +gdk_app_launch_context_class_init (GdkAppLaunchContextClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GAppLaunchContextClass *context_class = G_APP_LAUNCH_CONTEXT_CLASS (klass); + + gobject_class->finalize = gdk_app_launch_context_finalize; + + context_class->get_display = get_display; + context_class->get_startup_notify_id = _gdk_windowing_get_startup_notify_id; + context_class->launch_failed = _gdk_windowing_launch_failed; + + g_type_class_add_private (klass, sizeof (GdkAppLaunchContextPrivate)); +} + +static void +gdk_app_launch_context_init (GdkAppLaunchContext *context) +{ + context->priv = G_TYPE_INSTANCE_GET_PRIVATE (context, + GDK_TYPE_APP_LAUNCH_CONTEXT, + GdkAppLaunchContextPrivate); + context->priv->workspace = -1; +} + +void +gdk_app_launch_context_set_display (GdkAppLaunchContext *context, + GdkDisplay *display) +{ + if (context->priv->display) + { + g_object_unref (context->priv->display); + context->priv->display = NULL; + } + + if (display) + context->priv->display = g_object_ref (display); +} +void +gdk_app_launch_context_set_screen (GdkAppLaunchContext *context, + GdkScreen *screen) +{ + if (context->priv->screen) + { + g_object_unref (context->priv->screen); + context->priv->screen = NULL; + } + + if (screen) + context->priv->screen = g_object_ref (screen); +} + + +void +gdk_app_launch_context_set_desktop (GdkAppLaunchContext *context, + gint desktop) +{ + context->priv->workspace = desktop; +} + +void +gdk_app_launch_context_set_timestamp (GdkAppLaunchContext *context, + guint32 timestamp) +{ + context->priv->timestamp = timestamp; +} + +void +gdk_app_launch_context_set_icon (GdkAppLaunchContext *context, + GIcon *icon) +{ + if (context->priv->icon) + { + g_object_unref (context->priv->icon); + context->priv->icon = NULL; + } + + if (icon) + context->priv->icon = g_object_ref (icon); +} + +void +gdk_app_launch_context_set_icon_name (GdkAppLaunchContext *context, + const char *icon_name) +{ + g_free (context->priv->icon_name); + context->priv->icon_name = g_strdup (icon_name); +} + +GdkAppLaunchContext * +gdk_app_launch_context_new (void) +{ + GdkAppLaunchContext *context; + + context = g_object_new (gdk_app_launch_context_get_type (), NULL); + return context; +} + + +#define __GDK_APP_LAUNCH_CONTEXT_C__ +#include "gdkaliasdef.c" diff --git a/gdk/gdkapplaunchcontext.h b/gdk/gdkapplaunchcontext.h new file mode 100644 index 0000000000..70b814cfbf --- /dev/null +++ b/gdk/gdkapplaunchcontext.h @@ -0,0 +1,75 @@ +/* gdkapplaunchcontext.h - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson +*/ + +#ifndef __GDK_APP_LAUNCH_CONTEXT_H__ +#define __GDK_APP_LAUNCH_CONTEXT_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GDK_TYPE_APP_LAUNCH_CONTEXT (gdk_app_launch_context_get_type ()) +#define GDK_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_APP_LAUNCH_CONTEXT, GdkAppLaunchContext)) +#define GDK_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_APP_LAUNCH_CONTEXT, GdkAppLaunchContextClass)) +#define GDK_IS_APP_LAUNCH_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDGDK_TYPE_APP_LAUNCH_CONTEXT)) +#define GDK_IS_APP_LAUNCH_CONTEXT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_APP_LAUNCH_CONTEXT)) +#define GDK_APP_LAUNCH_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_APP_LAUNCH_CONTEXT, GdkAppLaunchContextClass)) + +typedef struct GdkAppLaunchContext GdkAppLaunchContext; +typedef struct GdkAppLaunchContextClass GdkAppLaunchContextClass; +typedef struct GdkAppLaunchContextPrivate GdkAppLaunchContextPrivate; + +struct GdkAppLaunchContext +{ + GAppLaunchContext parent_instance; + + GdkAppLaunchContextPrivate *priv; +}; + +struct GdkAppLaunchContextClass +{ + GAppLaunchContextClass parent_class; +}; + +GType gdk_app_launch_context_get_type (void); + +GdkAppLaunchContext *gdk_app_launch_context_new (void); +void gdk_app_launch_context_set_display (GdkAppLaunchContext *context, + GdkDisplay *display); +void gdk_app_launch_context_set_screen (GdkAppLaunchContext *context, + GdkScreen *screen); +void gdk_app_launch_context_set_desktop (GdkAppLaunchContext *context, + gint desktop); +void gdk_app_launch_context_set_timestamp (GdkAppLaunchContext *context, + guint32 timestamp); +void gdk_app_launch_context_set_icon (GdkAppLaunchContext *context, + GIcon *icon); +void gdk_app_launch_context_set_icon_name (GdkAppLaunchContext *context, + const char *icon_name); + +G_END_DECLS + +#endif /* __GDK_APP_LAUNCH_CONTEXT_H__ */ + + diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index 45887c27c5..b8c84c51dc 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -409,6 +409,23 @@ void _gdk_windowing_gc_get_foreground (GdkGC *gc, void _gdk_windowing_gc_get_background (GdkGC *gc, GdkColor *color); +struct GdkAppLaunchContextPrivate +{ + GdkDisplay *display; + GdkScreen *screen; + gint workspace; + guint32 timestamp; + GIcon *icon; + char *icon_name; +}; + +char *_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, + GAppInfo *info, + GList *files); +void _gdk_windowing_launch_failed (GAppLaunchContext *context, + const char *startup_notify_id); + + /************************************ * Initialization and exit routines * ************************************/ diff --git a/gdk/quartz/Makefile.am b/gdk/quartz/Makefile.am index bbf7db58f8..2e2fbc888f 100644 --- a/gdk/quartz/Makefile.am +++ b/gdk/quartz/Makefile.am @@ -21,6 +21,7 @@ libgdk_quartz_la_SOURCES = \ GdkQuartzView.h \ GdkQuartzWindow.c \ GdkQuartzWindow.h \ + gdkapplaunchcontext-quartz.c \ gdkcolor-quartz.c \ gdkcursor-quartz.c \ gdkdisplay-quartz.c \ diff --git a/gdk/quartz/gdkapplaunchcontext-quartz.c b/gdk/quartz/gdkapplaunchcontext-quartz.c new file mode 100644 index 0000000000..769e4766ee --- /dev/null +++ b/gdk/quartz/gdkapplaunchcontext-quartz.c @@ -0,0 +1,42 @@ +/* gdkapplaunchcontext-quartz.c - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Matthias Clasen +*/ + +#include + +#include "gdkapplaunchcontext.h" + + +char * +_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, + GAppInfo *info, + GList *files) +{ + return NULL; +} + +void +_gdk_windowing_launch_failed (GAppLaunchContext *context, + const char *startup_notify_id) +{ +} + + diff --git a/gdk/win32/Makefile.am b/gdk/win32/Makefile.am index 08f58775a1..02f8c2f7aa 100644 --- a/gdk/win32/Makefile.am +++ b/gdk/win32/Makefile.am @@ -26,6 +26,7 @@ EXTRA_DIST += \ libgdk_win32_la_SOURCES = \ xcursors.h \ + gdkapplaunchcontext-win32.c \ gdkcolor-win32.c \ gdkcursor-win32.c \ gdkdisplay-win32.c \ diff --git a/gdk/win32/gdkapplaunchcontext-win32.c b/gdk/win32/gdkapplaunchcontext-win32.c new file mode 100644 index 0000000000..39a53e939c --- /dev/null +++ b/gdk/win32/gdkapplaunchcontext-win32.c @@ -0,0 +1,42 @@ +/* gdkapplaunchcontext-win32.c - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Matthias Clasen +*/ + +#include + +#include "gdkapplaunchcontext.h" + + +char * +_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, + GAppInfo *info, + GList *files) +{ + return NULL; +} + +void +_gdk_windowing_launch_failed (GAppLaunchContext *context, + const char *startup_notify_id) +{ +} + + diff --git a/gdk/x11/Makefile.am b/gdk/x11/Makefile.am index 11b284bd18..eb88a70cd0 100644 --- a/gdk/x11/Makefile.am +++ b/gdk/x11/Makefile.am @@ -19,6 +19,7 @@ noinst_LTLIBRARIES = libgdk-x11.la libgdk_x11_la_SOURCES = \ MwmUtil.h \ + gdkapplaunchcontext-x11.c \ gdkasync.c \ gdkasync.h \ gdkcolor-x11.c \ diff --git a/gdk/x11/gdkapplaunchcontext-x11.c b/gdk/x11/gdkapplaunchcontext-x11.c new file mode 100644 index 0000000000..17d3340de6 --- /dev/null +++ b/gdk/x11/gdkapplaunchcontext-x11.c @@ -0,0 +1,424 @@ +/* gdkapplaunchcontext-x11.c - Gtk+ implementation for GAppLaunchContext + + Copyright (C) 2007 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Alexander Larsson +*/ + +#include + +#include +#include + +#include +#include +#include + +#include "gdkx.h" +#include "gdkapplaunchcontext.h" +#include "gdkscreen.h" +#include "gdkinternals.h" +#include "gdkintl.h" +#include "gdkalias.h" + + +static char * +get_display_name (GFile *file) +{ + GFileInfo *info; + char *name, *tmp; + + /* This does sync I/O, which isn't ideal. + * It should probably use the NautilusFile machinery + */ + + name = NULL; + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STD_DISPLAY_NAME, 0, NULL, NULL); + if (info) + { + name = g_strdup (g_file_info_get_display_name (info)); + g_object_unref (info); + } + + if (name == NULL) + { + name = g_file_get_basename (file); + if (!g_utf8_validate (name, -1, NULL)) + { + tmp = name; + name = + g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, + TRUE); + g_free (tmp); + } + } + + return name; +} + +static GIcon * +get_icon (GFile *file) +{ + GFileInfo *info; + GIcon *icon; + + icon = NULL; + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STD_ICON, 0, NULL, NULL); + if (info) + { + icon = g_file_info_get_icon (info); + if (icon) + g_object_ref (icon); + g_object_unref (info); + } + + return icon; +} + +static char * +gicon_to_string (GIcon *icon) +{ + GFile *file; + const char *const *names; + + if (G_IS_FILE_ICON (icon)) + { + file = g_file_icon_get_file (G_FILE_ICON (icon)); + if (file) + return g_file_get_path (file); + } + else if (G_IS_THEMED_ICON (icon)) + { + names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + if (names) + return g_strdup (names[0]); + } + + return NULL; +} + +static void +end_startup_notification (GdkDisplay *display, + const char *startup_id) +{ + gdk_x11_display_broadcast_startup_message (display, "remove", + "ID", startup_id, + NULL); +} + + +/* This should be fairly long, as it's confusing to users if a startup + * ends when it shouldn't (it appears that the startup failed, and + * they have to relaunch the app). Also the timeout only matters when + * there are bugs and apps don't end their own startup sequence. + * + * This timeout is a "last resort" timeout that ignores whether the + * startup sequence has shown activity or not. Metacity and the + * tasklist have smarter, and correspondingly able-to-be-shorter + * timeouts. The reason our timeout is dumb is that we don't monitor + * the sequence (don't use an SnMonitorContext) + */ +#define STARTUP_TIMEOUT_LENGTH_SECONDS 30 +#define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000) + +typedef struct +{ + GdkDisplay *display; + char *startup_id; + GTimeVal time; +} StartupNotificationData; + +static void +free_startup_notification_data (gpointer data) +{ + StartupNotificationData *sn_data = data; + + g_object_unref (sn_data->display); + g_free (sn_data->startup_id); + g_free (sn_data); +} + +typedef struct +{ + GSList *contexts; + guint timeout_id; +} StartupTimeoutData; + +static void +free_startup_timeout (void *data) +{ + StartupTimeoutData *std; + + std = data; + + g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL); + g_slist_free (std->contexts); + + if (std->timeout_id != 0) + { + g_source_remove (std->timeout_id); + std->timeout_id = 0; + } + + g_free (std); +} + +static gboolean +startup_timeout (void *data) +{ + StartupTimeoutData *std; + GSList *tmp; + GTimeVal now; + int min_timeout; + + std = data; + + min_timeout = STARTUP_TIMEOUT_LENGTH; + + g_get_current_time (&now); + + tmp = std->contexts; + while (tmp != NULL) + { + StartupNotificationData *sn_data; + GSList *next; + double elapsed; + + sn_data = tmp->data; + next = tmp->next; + + elapsed = + ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC + + (now.tv_usec - sn_data->time.tv_usec))) / 1000.0; + + if (elapsed >= STARTUP_TIMEOUT_LENGTH) + { + std->contexts = g_slist_remove (std->contexts, sn_data); + end_startup_notification (sn_data->display, sn_data->startup_id); + free_startup_notification_data (sn_data); + } + else + { + min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed)); + } + + tmp = next; + } + + if (std->contexts == NULL) + std->timeout_id = 0; + else + std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std); + + /* always remove this one, but we may have reinstalled another one. */ + return FALSE; +} + + +static void +add_startup_timeout (GdkScreen *screen, + const char *startup_id) +{ + StartupTimeoutData *data; + StartupNotificationData *sn_data; + + data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data"); + + if (data == NULL) + { + data = g_new (StartupTimeoutData, 1); + data->contexts = NULL; + data->timeout_id = 0; + + g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data", + data, free_startup_timeout); + } + + sn_data = g_new (StartupNotificationData, 1); + sn_data->display = g_object_ref (gdk_screen_get_display (screen)); + sn_data->startup_id = g_strdup (startup_id); + g_get_current_time (&sn_data->time); + + data->contexts = g_slist_prepend (data->contexts, sn_data); + + if (data->timeout_id == 0) + data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS, + startup_timeout, data); +} + + +char * +_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context, + GAppInfo *info, + GList *files) +{ + static int sequence = 0; + GdkAppLaunchContextPrivate *priv; + GdkDisplay *display; + GdkScreen *screen; + int files_count; + char *description; + char *icon_name; + const char *binary_name; + char *screen_str; + char *workspace_str; + GIcon *icon; + guint32 timestamp; + char *startup_id; + + priv = GDK_APP_LAUNCH_CONTEXT (context)->priv; + + if (priv->screen) + { + screen = priv->screen; + display = gdk_screen_get_display (priv->screen); + } + else if (priv->display) + { + display = priv->display; + screen = gdk_display_get_default_screen (display); + } + else + { + display = gdk_display_get_default (); + screen = gdk_display_get_default_screen (display); + } + + files_count = g_list_length (files); + if (files_count == 0) + description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info)); + else if (files_count == 1) + description = g_strdup_printf (_("Opening %s"), get_display_name (files->data)); + else + description = g_strdup_printf (dngettext (GETTEXT_PACKAGE, + "Opening %d Item", + "Opening %d Items", + files_count), files_count); + + icon_name = NULL; + if (priv->icon_name) + icon_name = g_strdup (priv->icon_name); + else + { + icon = NULL; + + if (priv->icon != NULL) + icon = g_object_ref (priv->icon); + else if (files_count == 1) + icon = get_icon (files->data); + + if (icon == NULL) + { + icon = g_app_info_get_icon (info); + g_object_ref (icon); + } + + if (icon) + icon_name = gicon_to_string (icon); + + g_object_unref (icon); + } + + binary_name = g_app_info_get_executable (info); + + timestamp = priv->timestamp; + if (timestamp == GDK_CURRENT_TIME) + timestamp = gdk_x11_display_get_user_time (display); + + screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen)); + if (priv->workspace > -1) + workspace_str = g_strdup_printf ("%d", priv->workspace); + else + workspace_str = NULL; + + + startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu", + g_get_prgname (), + (unsigned long)getpid (), + g_get_host_name (), + binary_name, + sequence++, + (unsigned long)timestamp); + + + gdk_x11_display_broadcast_startup_message (display, "new", + "ID", startup_id, + "NAME", g_app_info_get_name (info), + "SCREEN", screen_str, + "BIN", binary_name, + "ICON", icon_name, + "DESKTOP", workspace_str, + "DESCRIPTION", description, + "WMCLASS", NULL, /* FIXME */ + NULL); + + g_free (description); + g_free (screen_str); + g_free (workspace_str); + g_free (icon_name); + + add_startup_timeout (screen, startup_id); + + return startup_id; +} + + +void +_gdk_windowing_launch_failed (GAppLaunchContext *context, + const char *startup_notify_id) +{ + GdkAppLaunchContextPrivate *priv; + GdkScreen *screen; + StartupTimeoutData *data; + StartupNotificationData *sn_data; + GSList *l; + + priv = GDK_APP_LAUNCH_CONTEXT (context)->priv; + + if (priv->screen) + screen = priv->screen; + else if (priv->display) + screen = gdk_display_get_default_screen (priv->display); + else + screen = gdk_display_get_default_screen (gdk_display_get_default ()); + + data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data"); + + if (data) + { + for (l = data->contexts; l != NULL; l = l->next) + { + sn_data = l->data; + if (strcmp (startup_notify_id, sn_data->startup_id) == 0) + { + data->contexts = g_slist_remove (data->contexts, sn_data); + end_startup_notification (sn_data->display, sn_data->startup_id); + free_startup_notification_data (sn_data); + + break; + } + } + + if (data->contexts == NULL) + { + g_source_remove (data->timeout_id); + data->timeout_id = 0; + } + } +}