diff --git a/ChangeLog b/ChangeLog index f90e146e96..69728d6c3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2007-03-13 Emmanuele Bassi + + Apply patch by Vytautas Liuolia for changing the startup + notification id on a window in the X11 backend. (#347375) + + * gdk/gdk.h: + * gdk/gdkx.h: + * gdk/x11/gdkdisplay-x11.c: Add gdk_notify_startup_complete_wit_id() + and gdk_x11_display_get_startup_notification_id(). + + * gdk/gdkwindow.h: + * gdk/x11/gdkwindow-x11.c: Add gdk_window_set_startup_id(). + + * gtk/gtkwindow.h: + * gtk/gtkwindow.c: Add gtk_window_set_startup_id(), used to + change the startup notification id. + + (gtk_window_class_init), (gtk_window_init), + (gtk_window_set_property): Add write-only "startup-id" property + to GtkWindow. + + (gtk_window_realize): Set the startup notification id + on a GtkWindow if it's valid. + + (gtk_window_map): If we have another valid startup notification + id then finish the notification process. + 2007-03-13 Matthias Clasen * gtk/gtknotebook.c (gtk_notebook_real_insert_page): diff --git a/gdk/gdk.h b/gdk/gdk.h index 1a7a596c10..d84d304a74 100644 --- a/gdk/gdk.h +++ b/gdk/gdk.h @@ -177,6 +177,8 @@ gboolean gdk_event_send_client_message_for_display (GdkDisplay *display, void gdk_notify_startup_complete (void); +void gdk_notify_startup_complete_with_id (const gchar* startup_id); + /* Threading */ diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 8380aa56e3..be1fc2b1e3 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -482,6 +482,8 @@ void gdk_window_set_title (GdkWindow *window, const gchar *title); void gdk_window_set_role (GdkWindow *window, const gchar *role); +void gdk_window_set_startup_id (GdkWindow *window, + const gchar *startup_id); void gdk_window_set_transient_for (GdkWindow *window, GdkWindow *parent); void gdk_window_set_background (GdkWindow *window, diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c index fa1524e0ad..ace29b2cfc 100644 --- a/gdk/x11/gdkdisplay-x11.c +++ b/gdk/x11/gdkdisplay-x11.c @@ -1079,7 +1079,36 @@ gdk_notify_startup_complete (void) if (!G_LIKELY (display_x11->trusted_client)) return; - escaped_id = escape_for_xmessage (display_x11->startup_notification_id); + gdk_notify_startup_complete_with_id (display_x11->startup_notification_id); +} + +/** + * gdk_notify_startup_complete_with_id: + * @startup_id: a startup-notification identifier, for which notification + * process should be completed + * + * Indicates to the GUI environment that the application has finished + * loading, using a given identifier. + * + * GTK+ will call this function automatically for #GtkWindow with custom + * startup-notification identifier unless + * gtk_window_set_auto_startup_notification() is called to disable + * that feature. + * + * Since: 2.12 + **/ +void +gdk_notify_startup_complete_with_id (const gchar* startup_id) +{ + GdkDisplay *display; + gchar *escaped_id; + gchar *message; + + display = gdk_display_get_default (); + if (!display) + return; + + escaped_id = escape_for_xmessage (startup_id); message = g_strdup_printf ("remove: ID=%s", escaped_id); g_free (escaped_id); @@ -1091,7 +1120,6 @@ gdk_notify_startup_complete (void) g_free (message); } - /** * gdk_display_supports_selection_notification: * @display: a #GdkDisplay @@ -1291,5 +1319,20 @@ gdk_display_supports_input_shapes (GdkDisplay *display) } +/** + * gdk_x11_display_get_startup_notification_id: + * @display: a #GdkDisplay + * + * Returns: the startup notification ID for + * @display. + * + * Since: 2.12 + */ +G_CONST_RETURN gchar * +gdk_x11_display_get_startup_notification_id (GdkDisplay *display) +{ + return GDK_DISPLAY_X11 (display)->startup_notification_id; +} + #define __GDK_DISPLAY_X11_C__ #include "gdkaliasdef.c" diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index dc46434775..c0226a6815 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -2896,6 +2896,40 @@ gdk_window_set_role (GdkWindow *window, } } +/** + * gdk_window_set_startup_id: + * @window: a toplevel #GdkWindow + * @startup_id: a string with startup-notification identifier + * + * When using GTK+, typically you should use gtk_window_set_startup_id() + * instead of this low-level function. + * + * Since: 2.12 + * + **/ +void +gdk_window_set_startup_id (GdkWindow *window, + const gchar *startup_id) +{ + GdkDisplay *display; + + g_return_if_fail (GDK_IS_WINDOW (window)); + + display = gdk_drawable_get_display (window); + + if (!GDK_WINDOW_DESTROYED (window)) + { + if (startup_id) + XChangeProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID"), + gdk_x11_get_xatom_by_name_for_display (display, "UTF8_STRING"), 8, + PropModeReplace, startup_id, strlen (startup_id)); + else + XDeleteProperty (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XID (window), + gdk_x11_get_xatom_by_name_for_display (display, "_NET_STARTUP_ID")); + } +} + /** * gdk_window_set_transient_for: * @window: a toplevel #GdkWindow diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h index f07a5883e7..9563e1b814 100644 --- a/gdk/x11/gdkx.h +++ b/gdk/x11/gdkx.h @@ -142,6 +142,8 @@ gpointer gdk_xid_table_lookup_for_display (GdkDisplay *display, guint32 gdk_x11_get_server_time (GdkWindow *window); guint32 gdk_x11_display_get_user_time (GdkDisplay *display); +G_CONST_RETURN gchar *gdk_x11_display_get_startup_notification_id (GdkDisplay *display); + void gdk_x11_display_set_cursor_theme (GdkDisplay *display, const gchar *theme, const gint size); diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 3766f38e18..b780c61c72 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include "gdk/gdk.h" #include "gdk/gdkkeysyms.h" @@ -95,6 +97,9 @@ enum { PROP_IS_ACTIVE, PROP_HAS_TOPLEVEL_FOCUS, + /* Writeonly properties */ + PROP_STARTUP_ID, + LAST_ARG }; @@ -177,6 +182,8 @@ struct _GtkWindowPrivate guint reset_type_hint : 1; GdkWindowTypeHint type_hint; + + gchar *startup_id; }; static void gtk_window_dispose (GObject *object); @@ -344,6 +351,33 @@ add_arrow_bindings (GtkBindingSet *binding_set, GTK_TYPE_DIRECTION_TYPE, direction); } +static guint32 +extract_time_from_startup_id (const gchar* startup_id) +{ + gchar *timestr = g_strrstr (startup_id, "_TIME"); + guint32 retval = GDK_CURRENT_TIME; + + if (timestr) + { + gchar *end; + guint32 timestamp; + + /* Skip past the "_TIME" part */ + timestr += 5; + + timestamp = strtoul (timestr, &end, 0); + if (end != timestr && errno == 0) + retval = timestamp; + } + + return retval; +} + +static gboolean +startup_id_is_fake (const gchar* startup_id) +{ + return strncmp (startup_id, "_TIME", 5) == 0; +} static void gtk_window_class_init (GtkWindowClass *klass) @@ -429,6 +463,23 @@ gtk_window_class_init (GtkWindowClass *klass) P_("Unique identifier for the window to be used when restoring a session"), NULL, GTK_PARAM_READWRITE)); + + /** + * GtkWindow:startup-id: + * + * The :startup-id is a write-only property for setting window's + * startup notification identifier. See gtk_window_set_startup_id() + * for more details. + * + * Since: 2.12 + */ + g_object_class_install_property (gobject_class, + PROP_ROLE, + g_param_spec_string ("startup-id", + P_("Startup ID"), + P_("Unique startup identifier for the window used by startup-notification"), + NULL, + GTK_PARAM_WRITABLE)); g_object_class_install_property (gobject_class, PROP_ALLOW_SHRINK, @@ -810,6 +861,7 @@ gtk_window_init (GtkWindow *window) priv->focus_on_map = TRUE; priv->deletable = TRUE; priv->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL; + priv->startup_id = NULL; colormap = _gtk_widget_peek_colormap (); if (colormap) @@ -846,6 +898,9 @@ gtk_window_set_property (GObject *object, case PROP_ROLE: gtk_window_set_role (window, g_value_get_string (value)); break; + case PROP_STARTUP_ID: + gtk_window_set_startup_id (window, g_value_get_string (value)); + break; case PROP_ALLOW_SHRINK: window->allow_shrink = g_value_get_boolean (value); gtk_widget_queue_resize (GTK_WIDGET (window)); @@ -1200,6 +1255,60 @@ gtk_window_set_role (GtkWindow *window, g_object_notify (G_OBJECT (window), "role"); } +/** + * gtk_window_set_startup_id: + * @window: a #GtkWindow + * @startup_id: a string with startup-notification identifier + * + * Startup notification identifiers are used by desktop environment to + * track application startup, to provide user feedback and other + * features. This function changes the corresponding property on the + * underlying GdkWindow. Normally, startup identifier is managed + * automatically and you should only use this function in special cases + * like transferring focus from other processes. You should use this + * function before calling gtk_window_present() or any equivalent + * function generating a window map event. + * + * This function is only useful on X11, not with other GTK+ targets. + * + * Since: 2.12 + **/ +void +gtk_window_set_startup_id (GtkWindow *window, + const gchar *startup_id) +{ + GtkWindowPrivate *priv = GTK_WINDOW_GET_PRIVATE (window); + + g_return_if_fail (GTK_IS_WINDOW (window)); + + g_free (priv->startup_id); + priv->startup_id = g_strdup (startup_id); + + if (GTK_WIDGET_REALIZED (window)) + { + /* Here we differentiate real and "fake" startup notification IDs, + * constructed on purpose just to pass interaction timestamp + */ + if (startup_id_is_fake (priv->startup_id)) + { + guint32 timestamp = extract_time_from_startup_id (priv->startup_id); + + gtk_window_present_with_time (window, timestamp); + } + else + { + gdk_window_set_startup_id (GTK_WIDGET (window)->window, + priv->startup_id); + + /* If window is mapped, terminate the startup-notification too */ + if (GTK_WIDGET_MAPPED (window) && !disable_startup_notification) + gdk_notify_startup_complete_with_id (priv->startup_id); + } + } + + g_object_notify (G_OBJECT (window), "startup-id"); +} + /** * gtk_window_get_role: * @window: a #GtkWindow @@ -4149,11 +4258,22 @@ gtk_window_map (GtkWidget *widget) if (window->frame) gdk_window_show (window->frame); - if (!disable_startup_notification && - !sent_startup_notification) + if (!disable_startup_notification) { - sent_startup_notification = TRUE; - gdk_notify_startup_complete (); + /* Do we have a custom startup-notification id? */ + if (priv->startup_id != NULL) + { + /* Make sure we have a "real" id */ + if (!startup_id_is_fake (priv->startup_id)) + gdk_notify_startup_complete_with_id (priv->startup_id); + + priv->startup_id = NULL; + } + else if (!sent_startup_notification) + { + sent_startup_notification = TRUE; + gdk_notify_startup_complete (); + } } } @@ -4223,7 +4343,6 @@ gtk_window_realize (GtkWidget *widget) GtkWindowPrivate *priv; window = GTK_WINDOW (widget); - priv = GTK_WINDOW_GET_PRIVATE (window); /* ensure widget tree is properly size allocated */ @@ -4377,6 +4496,17 @@ gtk_window_realize (GtkWidget *widget) gdk_window_set_modal_hint (widget->window, TRUE); else gdk_window_set_modal_hint (widget->window, FALSE); + + if (priv->startup_id) + { +#ifdef GDK_WINDOWING_X11 + guint32 timestamp = extract_time_from_startup_id (priv->startup_id); + if (timestamp != GDK_CURRENT_TIME) + gdk_x11_window_set_user_time (widget->window, timestamp); +#endif + if (!startup_id_is_fake (priv->startup_id)) + gdk_window_set_startup_id (widget->window, priv->startup_id); + } /* Icons */ gtk_window_realize_icon (window); diff --git a/gtk/gtkwindow.h b/gtk/gtkwindow.h index 4d4fc53d7e..c1ff6892d3 100644 --- a/gtk/gtkwindow.h +++ b/gtk/gtkwindow.h @@ -178,6 +178,8 @@ void gtk_window_set_wmclass (GtkWindow *window, const gchar *wmclass_class); void gtk_window_set_role (GtkWindow *window, const gchar *role); +void gtk_window_set_startup_id (GtkWindow *window, + const gchar *startup_id); G_CONST_RETURN gchar *gtk_window_get_role (GtkWindow *window); void gtk_window_add_accel_group (GtkWindow *window, GtkAccelGroup *accel_group);