Index: gdk/gdkevents.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkevents.h,v
retrieving revision 1.24
diff -u -r1.24 gdkevents.h
--- gdk/gdkevents.h	12 Jun 2005 05:32:24 -0000	1.24
+++ gdk/gdkevents.h	24 Jun 2005 20:24:56 -0000
@@ -33,6 +34,7 @@
 typedef struct _GdkEventDND         GdkEventDND;
 typedef struct _GdkEventWindowState GdkEventWindowState;
 typedef struct _GdkEventSetting     GdkEventSetting;
+typedef struct _GdkEventGrabBroken  GdkEventGrabBroken;
 
 typedef union  _GdkEvent	    GdkEvent;
 
@@ -118,7 +120,8 @@
   GDK_SCROLL            = 31,
   GDK_WINDOW_STATE      = 32,
   GDK_SETTING           = 33,
-  GDK_OWNER_CHANGE      = 34
+  GDK_OWNER_CHANGE      = 34,
+  GDK_GRAB_BROKEN       = 35
 } GdkEventType;
 
 /* Event masks. (Used to select what types of events a window
@@ -429,6 +432,13 @@
   GdkWindowState new_window_state;
 };
 
+struct _GdkEventGrabBroken {
+  GdkEventType type;
+  GdkWindow *window;
+  gint8 send_event;
+  gboolean keyboard;
+};
+
 /* Event types for DND */
 
 struct _GdkEventDND {
@@ -463,6 +473,7 @@
   GdkEventDND               dnd;
   GdkEventWindowState       window_state;
   GdkEventSetting           setting;
+  GdkEventGrabBroken        grab_broken;
 };
 
 GType     gdk_event_get_type            (void) G_GNUC_CONST;
Index: gdk/win32/gdkevents-win32.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/win32/gdkevents-win32.c,v
retrieving revision 1.152
diff -u -r1.152 gdkevents-win32.c
--- gdk/win32/gdkevents-win32.c	18 May 2005 14:53:00 -0000	1.152
+++ gdk/win32/gdkevents-win32.c	24 Jun 2005 20:24:57 -0000
@@ -2919,8 +2919,29 @@
 	 }
        break;
 
-    case WM_SETFOCUS:
     case WM_KILLFOCUS:
+      if (p_grab_window != NULL && !GDK_WINDOW_DESTROYED (p_grab_window))
+	{
+	  event = gdk_event_new (GDK_GRAB_BROKEN);
+	  event->grab_broken.window = p_grab_window;
+	  event->grab_broken.send_event = 0;
+	  event->grab_broken.keyboard = FALSE;
+	  
+          append_event (display, event);
+	}
+      if (k_grab_window != NULL && !GDK_WINDOW_DESTROYED (k_grab_window) 
+	  && k_grab_window != p_grab_window)
+	{
+	  event = gdk_event_new (GDK_GRAB_BROKEN);
+	  event->grab_broken.window = k_grab_window;
+	  event->grab_broken.send_event = 0;
+	  event->grab_broken.keyboard = TRUE;
+
+          append_event (display, event);
+	}
+
+      /* fallthrough */
+    case WM_SETFOCUS:
       if (k_grab_window != NULL && !k_grab_owner_events)
 	break;
 
Index: gdk/x11/gdkmain-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkmain-x11.c,v
retrieving revision 1.165
diff -u -r1.165 gdkmain-x11.c
--- gdk/x11/gdkmain-x11.c	17 Jun 2005 17:26:24 -0000	1.165
+++ gdk/x11/gdkmain-x11.c	24 Jun 2005 20:24:57 -0000
@@ -137,6 +137,21 @@
   return 0;
 }
 
+static void
+generate_grab_broken_event (GdkWindow *window,
+			    gboolean   keyboard)
+{
+  GdkEvent event;
+  
+  event.type = GDK_GRAB_BROKEN;
+  event.grab_broken.window = window;
+  event.grab_broken.send_event = 0;
+  event.grab_broken.keyboard = keyboard;
+  
+  g_print ("grab broken\n");
+  gdk_event_put (&event);
+}
+
 /*
  *--------------------------------------------------------------
  * gdk_pointer_grab
@@ -237,6 +252,10 @@
   if (return_val == GrabSuccess)
     {
       GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (GDK_WINDOW_DISPLAY (window));
+      if (display_x11->pointer_xgrab_window != NULL)
+	generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
+				    FALSE);
+
       display_x11->pointer_xgrab_window = (GdkWindowObject *)window;
       display_x11->pointer_xgrab_serial = serial;
       display_x11->pointer_xgrab_owner_events = owner_events;
@@ -334,6 +353,11 @@
   if (return_val == GrabSuccess)
     {
       GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (window));
+
+      if (display_x11->keyboard_xgrab_window != NULL)
+	generate_grab_broken_event (GDK_WINDOW (display_x11->keyboard_xgrab_window),
+				    TRUE);
+
       display_x11->keyboard_xgrab_window = (GdkWindowObject *)window;
       display_x11->keyboard_xgrab_serial = serial;
       display_x11->keyboard_xgrab_owner_events = owner_events;
@@ -405,7 +429,11 @@
 	tmp = tmp->parent;
 
       if (tmp)
-	display_x11->pointer_xgrab_window = NULL;  
+	{	  
+	  generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
+				      FALSE);
+	  display_x11->pointer_xgrab_window = NULL;  
+	}
     }
 
   if (display_x11->keyboard_xgrab_window &&
@@ -419,7 +447,11 @@
 	tmp = tmp->parent;
 
       if (tmp)
-	display_x11->keyboard_xgrab_window = NULL;  
+	{
+	  generate_grab_broken_event (GDK_WINDOW (display_x11->keyboard_xgrab_window),
+				      TRUE);
+	  display_x11->keyboard_xgrab_window = NULL;  
+	}
     }
 }
 
@@ -436,10 +468,18 @@
   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (window));
   
   if ((GdkWindowObject *)window == display_x11->pointer_xgrab_window)
-     display_x11->pointer_xgrab_window = NULL;
+    {
+      generate_grab_broken_event (GDK_WINDOW (display_x11->pointer_xgrab_window),
+				  FALSE);
+      display_x11->pointer_xgrab_window = NULL;
+    }
 
   if ((GdkWindowObject *)window ==  display_x11->keyboard_xgrab_window)
-     display_x11->keyboard_xgrab_window = NULL;
+    {
+      generate_grab_broken_event (GDK_WINDOW (display_x11->keyboard_xgrab_window),
+				  TRUE);
+      display_x11->keyboard_xgrab_window = NULL;
+    }
 }
 
 void
Index: gtk/gtkbutton.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkbutton.c,v
retrieving revision 1.128
diff -u -r1.128 gtkbutton.c
--- gtk/gtkbutton.c	23 Jun 2005 04:24:24 -0000	1.128
+++ gtk/gtkbutton.c	24 Jun 2005 20:24:57 -0000
@@ -110,6 +110,8 @@
 				       GdkEventButton   *event);
 static gint gtk_button_button_release (GtkWidget        *widget,
 				       GdkEventButton   *event);
+static gint gtk_button_grab_broken    (GtkWidget        *widget,
+				       GdkEventAny      *event);
 static gint gtk_button_key_release    (GtkWidget        *widget,
 				       GdkEventKey      *event);
 static gint gtk_button_enter_notify   (GtkWidget        *widget,
@@ -199,6 +201,7 @@
   widget_class->expose_event = gtk_button_expose;
   widget_class->button_press_event = gtk_button_button_press;
   widget_class->button_release_event = gtk_button_button_release;
+  widget_class->grab_broken_event = gtk_button_grab_broken;
   widget_class->key_release_event = gtk_button_key_release;
   widget_class->enter_notify_event = gtk_button_enter_notify;
   widget_class->leave_notify_event = gtk_button_leave_notify;
@@ -1255,6 +1258,29 @@
     {
       button = GTK_BUTTON (widget);
       gtk_button_released (button);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_button_grab_broken (GtkWidget   *widget,
+			GdkEventAny *event)
+{
+  GtkButton *button = GTK_BUTTON (widget);
+  gboolean save_in;
+  
+  /* Simulate a button release without the pointer in the button */
+  if (button->button_down)
+    {
+      save_in = button->in_button;
+      button->in_button = FALSE;
+      gtk_button_released (button);
+      if (save_in != button->in_button)
+	{
+	  button->in_button = save_in;
+	  gtk_button_update_state (button);
+	}
     }
 
   return TRUE;
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.258
diff -u -r1.258 gtkmain.c
--- gtk/gtkmain.c	2 May 2005 13:41:32 -0000	1.258
+++ gtk/gtkmain.c	24 Jun 2005 20:24:58 -0000
@@ -1369,6 +1369,7 @@
     case GDK_CLIENT_EVENT:
     case GDK_VISIBILITY_NOTIFY:
     case GDK_WINDOW_STATE:
+    case GDK_GRAB_BROKEN:
       gtk_widget_event (event_widget, event);
       break;
 
Index: gtk/gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.94
diff -u -r1.94 gtkmenushell.c
--- gtk/gtkmenushell.c	10 Jun 2005 04:45:40 -0000	1.94
+++ gtk/gtkmenushell.c	24 Jun 2005 20:24:58 -0000
@@ -166,6 +166,8 @@
 					      GdkEventCrossing  *event);
 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
 					      GdkScreen         *previous_screen);
+static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
+					      GdkEventGrabBroken *event);
 static void gtk_menu_shell_add               (GtkContainer      *container,
 					      GtkWidget         *widget);
 static void gtk_menu_shell_remove            (GtkContainer      *container,
@@ -254,6 +256,7 @@
   widget_class->realize = gtk_menu_shell_realize;
   widget_class->button_press_event = gtk_menu_shell_button_press;
   widget_class->button_release_event = gtk_menu_shell_button_release;
+  widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
   widget_class->key_press_event = gtk_menu_shell_key_press;
   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
@@ -599,6 +602,27 @@
 	  gtk_menu_shell_deactivate (menu_shell);
 	  g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
 	}
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_menu_shell_grab_broken (GtkWidget          *widget,
+			    GdkEventGrabBroken *event)
+{
+  GtkMenuShell *menu_shell;
+  GtkWidget *menu_item;
+  gint deactivate;
+
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
+  g_return_val_if_fail (event != NULL, FALSE);
+
+  menu_shell = GTK_MENU_SHELL (widget);
+  if (menu_shell->active)
+    {
+      gtk_menu_shell_deactivate (menu_shell);
+      g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
     }
 
   return TRUE;
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.405
diff -u -r1.405 gtkwidget.c
--- gtk/gtkwidget.c	11 Jun 2005 05:51:55 -0000	1.405
+++ gtk/gtkwidget.c	24 Jun 2005 20:24:59 -0000
@@ -120,6 +120,7 @@
   ACCEL_CLOSURES_CHANGED,
   SCREEN_CHANGED,
   CAN_ACTIVATE_ACCEL,
+  GRAB_BROKEN,
   LAST_SIGNAL
 };
 
@@ -398,6 +399,7 @@
   klass->drag_data_received = NULL;
   klass->screen_changed = NULL;
   klass->can_activate_accel = gtk_widget_real_can_activate_accel;
+  klass->grab_broken_event = NULL;
 
   klass->show_help = gtk_widget_real_show_help;
   
@@ -1324,6 +1326,32 @@
 		  _gtk_marshal_BOOLEAN__BOXED,
 		  G_TYPE_BOOLEAN, 1,
 		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+  /**
+   * GtkWidget::grab-broken:
+   * @widget: the object which received the signal
+   * @event: the #GdkEventGrabBroken event
+   *
+   * Emitted when a pointer or keyboard grab on a window belonging 
+   * to @widget gets broken. 
+   * 
+   * On X11, this happens when the grab window becomes unviewable 
+   * (i.e. it or one of its ancestors is unmapped), or if the same 
+   * application grabs the pointer or keyboard again.
+   *
+   * Returns: %TRUE to stop other handlers from being invoked for the event. 
+   *   %FALSE to propagate the event further.
+   *
+   * Since: 2.8
+   */
+  widget_signals[GRAB_BROKEN] =
+    g_signal_new ("grab_broken_event",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GtkWidgetClass, grab_broken_event),
+		  _gtk_boolean_handled_accumulator, NULL,
+		  _gtk_marshal_BOOLEAN__BOXED,
+		  G_TYPE_BOOLEAN, 1,
+		  GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 /**
  * GtkWidget::popup-menu
  * @widget: the object which received the signal
@@ -3693,6 +3721,9 @@
 	  break;
 	case GDK_VISIBILITY_NOTIFY:
 	  signal_num = VISIBILITY_NOTIFY_EVENT;
+	  break;
+	case GDK_GRAB_BROKEN:
+	  signal_num = GRAB_BROKEN;
 	  break;
 	default:
 	  g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.155
diff -u -r1.155 gtkwidget.h
--- gtk/gtkwidget.h	6 May 2005 03:12:33 -0000	1.155
+++ gtk/gtkwidget.h	24 Jun 2005 20:24:59 -0000
@@ -403,8 +403,11 @@
   gboolean     (*can_activate_accel) (GtkWidget *widget,
                                       guint      signal_id);
 
+  /* Sent when a grab is broken. */
+  gboolean (*grab_broken_event) (GtkWidget	     *widget,
+                                 GdkEventGrabBroken  *event);
+
   /* Padding for future expansion */
-  void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
   void (*_gtk_reserved4) (void);
   void (*_gtk_reserved5) (void);
Index: docs/reference/gdk/gdk-sections.txt
===================================================================
RCS file: /cvs/gnome/gtk+/docs/reference/gdk/gdk-sections.txt,v
retrieving revision 1.60
diff -u -r1.60 gdk-sections.txt
--- docs/reference/gdk/gdk-sections.txt	21 Jun 2005 04:09:52 -0000	1.60
+++ docs/reference/gdk/gdk-sections.txt	24 Jun 2005 20:24:59 -0000
@@ -1101,6 +1101,7 @@
 GdkEventWindowState
 GdkEventSetting
 GdkEventOwnerChange
+GdkEventGrabBroken
 
 <SUBSECTION>
 GdkScrollDirection
Index: docs/reference/gdk/tmpl/event_structs.sgml
===================================================================
RCS file: /cvs/gnome/gtk+/docs/reference/gdk/tmpl/event_structs.sgml,v
retrieving revision 1.30
diff -u -r1.30 event_structs.sgml
--- docs/reference/gdk/tmpl/event_structs.sgml	20 Jun 2005 22:06:11 -0000	1.30
+++ docs/reference/gdk/tmpl/event_structs.sgml	24 Jun 2005 20:24:59 -0000
@@ -424,6 +424,22 @@
 @selection_time: the time at which the selection ownership was taken over
 @Since: 2.6
 
+<!-- ##### STRUCT GdkEventGrabBroken ##### -->
+<para>
+Generated when a pointer or keyboard grab is broken. On X11, this happens
+when the grab window becomes unviewable (i.e. it or one of its ancestors 
+is unmapped), or if the same application grabs the pointer or keyboard
+again.
+</para>
+
+@type: the type of the event (%GDK_GRAB_BROKEN) 
+@window: the window which received the event, i.e. the window
+ that previously owned the grab
+@send_event: %TRUE if the event was sent explicitly (e.g. using <function>XSendEvent</function>).
+@keyboard: %TRUE if a keyboard grab was broken, %FALSE if a pointer 
+  grab was broken
+@Since: 2.8
+
 <!-- ##### ENUM GdkScrollDirection ##### -->
 <para>
 Specifies the direction for #GdkEventScroll. 
Index: docs/reference/gdk/tmpl/events.sgml
===================================================================
RCS file: /cvs/gnome/gtk+/docs/reference/gdk/tmpl/events.sgml,v
retrieving revision 1.23
diff -u -r1.23 events.sgml
--- docs/reference/gdk/tmpl/events.sgml	20 Jun 2005 22:06:11 -0000	1.23
+++ docs/reference/gdk/tmpl/events.sgml	24 Jun 2005 20:24:59 -0000
@@ -90,7 +90,9 @@
 @GDK_SETTING: a setting has been modified.
 @GDK_OWNER_CHANGE: the owner of a selection has changed. This event type
   was added in 2.6
+@GDK_GRAB_BROKEN: a pointer or keyboard grab was broken. This event type
+  was added in 2.8.
 
 <!-- ##### ENUM GdkEventMask ##### -->
 <para>
 
