Index: gdk/gdkdisplay.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkdisplay.h,v
retrieving revision 1.23
diff -p -u -r1.23 gdkdisplay.h
--- gdk/gdkdisplay.h	20 Feb 2006 01:36:48 -0000	1.23
+++ gdk/gdkdisplay.h	8 May 2006 23:26:07 -0000
@@ -114,6 +114,10 @@ void        gdk_display_pointer_ungrab  
 void        gdk_display_keyboard_ungrab    (GdkDisplay  *display,
 					    guint32      time_);
 gboolean    gdk_display_pointer_is_grabbed (GdkDisplay  *display);
+
+void        gdk_display_mark_button_press    (GdkDisplay  *display);
+gboolean    gdk_display_button_press_was_dnd (GdkDisplay  *display);
+
 void        gdk_display_beep               (GdkDisplay  *display);
 void        gdk_display_sync               (GdkDisplay  *display);
 void        gdk_display_flush              (GdkDisplay  *display);
Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.59
diff -p -u -r1.59 gdkwindow.h
--- gdk/gdkwindow.h	25 Apr 2006 14:27:28 -0000	1.59
+++ gdk/gdkwindow.h	8 May 2006 23:26:08 -0000
@@ -198,6 +198,42 @@ typedef enum
   GDK_WINDOW_EDGE_SOUTH_EAST  
 } GdkWindowEdge;
 
+/* Types of mouse actions, used by system to determine whether and when
+ * to raise, focus, etc.  These types and their meaning are taken from
+ * the freedesktop.org EWMH:
+ *   NONE:      No relevant mouse click-and-drag action.  This is used
+ *              when no action exists for a click at the given location
+ *              or when dragging would have no meaning (such as with
+ *              buttons).  It is also used on ButtonRelease if there was
+ *              not "threshold" mouse motion between ButtonPress and
+ *              ButtonRelease.
+ *   MISC:      A mouse click-and-drag action not listed below.  This
+ *              can also be used by a Client for actions that don't
+ *              fit into the other categories while waiting for this
+ *              list of action_types to be officially extended.
+ *              (EDITORIAL NOTE: This wording differs slightly from the EWMH
+ *               spec because GTK+ apps should fully support this spec and
+ *               thus should NOT make more general use of MISC)
+ *   DND:       Mouse action is or may be part of drag and drop
+ *   SELECTION: Mouse action is or may be part of selection (usually text
+ *              selection, but could include graphics or even be
+ *              exclusively graphics (think of office suites with
+ *              mixtures of graphics and text, for example))
+ *   SLIDING:   Mouse action is or may be part of moving a slider
+ *   WORKING:   Mouse action is or may be part of general working
+ *              (drawing in a photo editor, moving objects in a diagram
+ *              editor, etc.)
+ */
+typedef enum
+{
+  GDK_MOUSE_ACTION_NONE      = 0,
+  GDK_MOUSE_ACTION_MISC      = 1,
+  GDK_MOUSE_ACTION_DND       = 2,
+  GDK_MOUSE_ACTION_SELECTION = 3,
+  GDK_MOUSE_ACTION_SLIDING   = 4,
+  GDK_MOUSE_ACTION_WORKING   = 5
+} GdkMouseActionType;
+
 struct _GdkWindowAttr
 {
   gchar *title;
@@ -566,6 +602,14 @@ void          gdk_window_set_keep_below 
                                           gboolean         setting);
 
 void          gdk_window_register_dnd    (GdkWindow       *window);
+
+void          gdk_window_begin_mouse_action    (GdkWindow          *window,
+                                                GdkMouseActionType  action,
+                                                guint32             timestamp);
+void          gdk_window_end_mouse_action      (GdkWindow          *window,
+                                                GdkMouseActionType  action,
+                                                guint32             timestamp);
+void          gdk_window_mouse_motion_occurred (GdkWindow          *window);
 
 void gdk_window_begin_resize_drag (GdkWindow     *window,
                                    GdkWindowEdge  edge,
Index: gdk/x11/gdkdisplay-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.c,v
retrieving revision 1.70
diff -p -u -r1.70 gdkdisplay-x11.c
--- gdk/x11/gdkdisplay-x11.c	25 Apr 2006 14:27:30 -0000	1.70
+++ gdk/x11/gdkdisplay-x11.c	8 May 2006 23:26:08 -0000
@@ -94,7 +94,8 @@ static const char *const precache_atoms[
   "_NET_WM_WINDOW_TYPE",
   "_NET_WM_WINDOW_TYPE_NORMAL",
   "_NET_WM_USER_TIME",
-  "_NET_VIRTUAL_ROOTS"
+  "_NET_VIRTUAL_ROOTS",
+  "_NET_WM_MOUSE_ACTION"
 };
 
 G_DEFINE_TYPE (GdkDisplayX11, _gdk_display_x11, GDK_TYPE_DISPLAY);
@@ -252,6 +253,9 @@ gdk_display_open (const gchar *display_n
   /* We don't yet know a valid time. */
   display_x11->user_time = 0;
   
+  /* We haven't started a mouse action yet */
+  display_x11->mouse_action_begin_time = 0;
+  
 #ifdef HAVE_XKB
   {
     gint xkb_major = XkbMajorVersion;
@@ -542,6 +546,28 @@ gdk_display_pointer_is_grabbed (GdkDispl
   
   return (GDK_DISPLAY_X11 (display)->pointer_xgrab_window != NULL &&
 	  !GDK_DISPLAY_X11 (display)->pointer_xgrab_implicit);
+}
+
+void
+gdk_display_mark_button_press (GdkDisplay  *display)
+{
+  GdkDisplayX11 *display_x11;
+
+  g_return_if_fail (GDK_IS_DISPLAY (display));
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  display_x11->mouse_action_button_press_began_dnd = FALSE;
+}
+
+gboolean
+gdk_display_button_press_was_dnd (GdkDisplay  *display)
+{
+  GdkDisplayX11 *display_x11;
+
+  g_return_val_if_fail (GDK_IS_DISPLAY (display), FALSE);
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  return display_x11->mouse_action_button_press_began_dnd;
 }
 
 /**
Index: gdk/x11/gdkdisplay-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.h,v
retrieving revision 1.24
diff -p -u -r1.24 gdkdisplay-x11.h
--- gdk/x11/gdkdisplay-x11.h	20 Feb 2006 09:49:49 -0000	1.24
+++ gdk/x11/gdkdisplay-x11.h	8 May 2006 23:26:08 -0000
@@ -146,6 +146,12 @@ struct _GdkDisplayX11
   /* Time of most recent user interaction. */
   gulong user_time;
 
+  /* Information about mouse action operation; _NET_WM_MOUSE_ACTION protocol */
+  Time    mouse_action_begin_time;
+  Window  mouse_action_window;
+  guint   mouse_action_motion_occurred : 1;
+  guint   mouse_action_button_press_began_dnd : 1;
+
   /* Sets of atoms for DND */
   guint base_dnd_atoms_precached : 1;
   guint xdnd_atoms_precached : 1;
Index: gdk/x11/gdkwindow-x11.c
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkwindow-x11.c,v
retrieving revision 1.259
diff -p -u -r1.259 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	25 Apr 2006 14:27:30 -0000	1.259
+++ gdk/x11/gdkwindow-x11.c	8 May 2006 23:26:08 -0000
@@ -450,12 +450,13 @@ static void
 set_wm_protocols (GdkWindow *window)
 {
   GdkDisplay *display = gdk_drawable_get_display (window);
-  Atom protocols[4];
+  Atom protocols[5];
   int n = 0;
   
   protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_DELETE_WINDOW");
   protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS");
   protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_PING");
+  protocols[n++] = gdk_x11_get_xatom_by_name_for_display (display, "_NET_WM_MOUSE_ACTION");
 
 #ifdef HAVE_XSYNC
   if (GDK_DISPLAY_X11 (display)->use_sync)
@@ -4093,6 +4094,190 @@ gdk_x11_window_set_user_time (GdkWindow 
     display_x11->user_time = timestamp_long;
 
   toplevel->user_time = timestamp_long;
+}
+
+static void
+send_mouse_action_message (GdkWindow          *window,
+                           GdkDisplay         *display,
+                           GdkMouseActionType  action,
+                           guint32             timestamp,
+                           gboolean            is_button_press)
+{
+  XEvent xev;
+  GdkDisplayX11 *display_x11;
+
+  /* window MUST be a toplevel window */
+  g_assert (window == gdk_window_get_toplevel (window));
+
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  /* Spec says that ButtonReleases without mouse motion should have
+   * action changed to GDK_MOUSE_ACTION_NONE and Window Manager will
+   * rely on this to determine whether there was mouse motion.
+   */
+  if (!is_button_press && !display_x11->mouse_action_motion_occurred)
+    action = GDK_MOUSE_ACTION_NONE;
+  
+  xev.xclient.type = ClientMessage;
+  xev.xclient.serial = 0;
+  xev.xclient.send_event = True;
+  xev.xclient.window =  GDK_WINDOW_XROOTWIN (window);
+  xev.xclient.message_type =
+    gdk_x11_get_xatom_by_name_for_display (display,
+                                           "_NET_WM_MOUSE_ACTION");
+  xev.xclient.format = 32;
+  xev.xclient.data.l[0] = display_x11->mouse_action_begin_time;
+  if (is_button_press)
+    {
+      xev.xclient.data.l[1] = 0;         /* ButtonRelease timestamp unknown */
+      xev.xclient.data.l[3] = 0;         /* ButtonPress event_type = 0      */
+    }
+  else
+    {
+      xev.xclient.data.l[1] = timestamp; /* ButtonRelease timestamp         */
+      xev.xclient.data.l[3] = 1;         /* ButtonRelease event_type = 1    */
+    }
+  xev.xclient.data.l[2] = GDK_WINDOW_XID (window);
+  xev.xclient.data.l[4] = action;
+
+  XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
+              SubstructureRedirectMask | SubstructureNotifyMask,
+              &xev);
+}
+
+/**
+ * gdk_window_begin_mouse_action:
+ * @window:    A toplevel #GdkWindow
+ * @action:    Potential mouse action beginning
+ * @timestamp: timestamp of the ButtonPress event
+ *
+ * This function is called upon ButtonPress to hint to the system that
+ * a certain type of mouse action may be beginning.  Every call to
+ * this function should be matched by a corresponding call to
+ * gdk_window_end_mouse_action upon ButtonRelease.  The system may use
+ * this information, for example, to determine whether (and when) to
+ * raise or focus the window.
+ *
+ * On X, it is the responsibility of the window manager to handle
+ * this, and it is accomplished through the _NET_WM_MOUSE_ACTION
+ * protocol.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_begin_mouse_action    (GdkWindow          *window,
+                                  GdkMouseActionType  action,
+                                  guint32             timestamp)
+{
+  GdkDisplay *display;
+  GdkDisplayX11 *display_x11;
+
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  display = gdk_drawable_get_display (window);
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  /* DEBUGGING */
+  if (display_x11->mouse_action_begin_time != 0)
+    g_error ("We never sent a ButtonRelease for the last ButtonPress!\n");
+  /* DEBUGGING */
+
+  display_x11->mouse_action_begin_time = timestamp;
+  display_x11->mouse_action_window = window;
+  send_mouse_action_message (window, display, action, timestamp, TRUE);
+  display_x11->mouse_action_motion_occurred = FALSE;
+
+  if (action == GDK_MOUSE_ACTION_DND)
+    display_x11->mouse_action_button_press_began_dnd = TRUE;
+}
+
+/**
+ * gdk_window_end_mouse_action:
+ * @window: A toplevel #GdkWindow
+ * @action: Potential mouse action beginning
+ * @timestamp: timestamp of the ButtonRelease event
+ *
+ * This function is called upon ButtonRelease to hint to the system
+ * that a certain type of mouse action is over.  Every call to this
+ * function should be preceded by a corresponding call to
+ * gdk_window_begin_mouse_action upon ButtonPress (and with the same
+ * action field as used in this call).  The system may use this
+ * information, for example, to determine whether (and when) to raise
+ * or focus the window.
+ *
+ * On X, it is the responsibility of the window manager to handle
+ * this, and it is accomplished through the _NET_WM_MOUSE_ACTION
+ * protocol.
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_end_mouse_action    (GdkWindow          *window,
+                                GdkMouseActionType  action,
+                                guint32             timestamp)
+{
+  GdkDisplay *display;
+  GdkDisplayX11 *display_x11;
+
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  display = gdk_drawable_get_display (window);
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  /* DEBUGGING */
+  if (display_x11->mouse_action_begin_time == 0)
+    g_error ("We never sent a ButtonPress before this ButtonRelease!\n");
+  /* DEBUGGING */
+
+  /* Use display_x11->mouse_action_window instead of window, because
+   * ButtonRelease is going to occur on an override-redirect dnd
+   * window instead of the window where the ButtonPress occurred.
+   */
+  send_mouse_action_message (display_x11->mouse_action_window,
+                             display,
+                             action,
+                             timestamp,
+                             FALSE);
+
+  display_x11->mouse_action_begin_time = 0;
+}
+
+/**
+ * gdk_window_mouse_motion_occurred:
+ * @window: A toplevel #GdkWindow
+ *
+ * This function is called after a call to
+ * gdk_window_begin_mouse_action but before a call to
+ * gdk_window_end_mouse_action in order to specify that the mouse
+ * action really did occur (i.e. that there was sufficient mouse
+ * motion between ButtonPress and ButtonRelease).
+ *
+ * Since: 2.6
+ **/
+void
+gdk_window_mouse_motion_occurred (GdkWindow *window)
+{
+  GdkDisplay *display;
+  GdkDisplayX11 *display_x11;
+
+  g_return_if_fail (window != NULL);
+  g_return_if_fail (GDK_IS_WINDOW (window));
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  display = gdk_drawable_get_display (window);
+  display_x11 = GDK_DISPLAY_X11 (display);
+
+  display_x11->mouse_action_motion_occurred = TRUE;
 }
 
 #define GDK_SELECTION_MAX_SIZE(display)                                 \
Index: gtk/gtkdnd.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkdnd.c,v
retrieving revision 1.153
diff -p -u -r1.153 gtkdnd.c
--- gtk/gtkdnd.c	27 Apr 2006 05:16:43 -0000	1.153
+++ gtk/gtkdnd.c	8 May 2006 23:26:08 -0000
@@ -2163,6 +2163,8 @@ gtk_drag_begin_internal (GtkWidget      
   GdkDragContext *context;
   GtkWidget *ipc_widget;
   GdkCursor *cursor;
+
+  gdk_window_mouse_motion_occurred (gdk_window_get_toplevel (widget->window));
  
   ipc_widget = gtk_drag_get_ipc_widget (gtk_widget_get_screen (widget));
   
@@ -3532,6 +3534,10 @@ gtk_drag_source_event_cb (GtkWidget     
     case GDK_BUTTON_PRESS:
       if ((GDK_BUTTON1_MASK << (event->button.button - 1)) & site->start_button_mask)
 	{
+          GdkWindow *toplevel = gdk_window_get_toplevel (widget->window);
+          gdk_window_begin_mouse_action (toplevel,
+                                         GDK_MOUSE_ACTION_DND,
+                                         event->button.time);
 	  site->state |= (GDK_BUTTON1_MASK << (event->button.button - 1));
 	  site->x = event->button.x;
 	  site->y = event->button.y;
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.276
diff -p -u -r1.276 gtkmain.c
--- gtk/gtkmain.c	6 Apr 2006 03:29:48 -0000	1.276
+++ gtk/gtkmain.c	8 May 2006 23:26:08 -0000
@@ -137,6 +137,7 @@ const guint gtk_interface_age = GTK_INTE
 static guint gtk_main_loop_level = 0;
 static gint gtk_initialized = FALSE;
 static GList *current_events = NULL;
+static GdkMouseActionType current_mouse_action_type = GDK_MOUSE_ACTION_NONE;
 
 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
 
@@ -1398,8 +1399,32 @@ gtk_main_do_event (GdkEvent *event)
       gtk_widget_event (event_widget, event);
       break;
 
-    case GDK_SCROLL:
     case GDK_BUTTON_PRESS:
+      {
+      /* Record that we need to send a MOUSE_ACTION event for this */
+      GdkDisplay *gdk_display = gtk_widget_get_display (grab_widget);
+      gdk_display_mark_button_press (gdk_display);
+
+      /* Handle the button press normally */
+      gtk_propagate_event (grab_widget, event);
+
+      /* If no MOUSE_ACTION message was sent by a special handler, we
+       * need to do so here.
+       */
+      if (!gdk_display_button_press_was_dnd (gdk_display))
+        {
+          GdkWindow *toplevel = gdk_window_get_toplevel (grab_widget->window);
+          gdk_window_begin_mouse_action (toplevel,
+                                         GDK_MOUSE_ACTION_NONE,
+                                         event->button.time);
+          current_mouse_action_type = GDK_MOUSE_ACTION_NONE;
+        }
+      else
+        current_mouse_action_type = GDK_MOUSE_ACTION_DND;
+      }
+      break;
+
+    case GDK_SCROLL:
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
       gtk_propagate_event (grab_widget, event);
@@ -1414,9 +1439,21 @@ gtk_main_do_event (GdkEvent *event)
 	}
       /* else fall through */
     case GDK_MOTION_NOTIFY:
-    case GDK_BUTTON_RELEASE:
     case GDK_PROXIMITY_IN:
     case GDK_PROXIMITY_OUT:
+      gtk_propagate_event (grab_widget, event);
+      break;
+
+    case GDK_BUTTON_RELEASE:
+      {
+      /* Send the relevant MOUSE_ACTION message */
+      GdkWindow *toplevel = gdk_window_get_toplevel (grab_widget->window);
+      gdk_window_end_mouse_action (toplevel,
+                                   current_mouse_action_type,
+                                   event->button.time);
+      current_mouse_action_type = GDK_MOUSE_ACTION_NONE;
+      }
+      /* Handle the event */
       gtk_propagate_event (grab_widget, event);
       break;
       
