Index: gdk/gdkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/gdkwindow.h,v
retrieving revision 1.51
diff -p -u -r1.51 gdkwindow.h
--- gdk/gdkwindow.h	11 Jul 2004 13:26:57 -0000	1.51
+++ gdk/gdkwindow.h	12 Oct 2004 19:30:15 -0000
@@ -169,6 +169,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;
@@ -516,6 +552,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.47
diff -p -u -r1.47 gdkdisplay-x11.c
--- gdk/x11/gdkdisplay-x11.c	5 Sep 2004 15:09:55 -0000	1.47
+++ gdk/x11/gdkdisplay-x11.c	12 Oct 2004 19:30:15 -0000
@@ -89,6 +89,7 @@ static const char *const precache_atoms[
   "_NET_WM_WINDOW_TYPE",
   "_NET_WM_WINDOW_TYPE_NORMAL",
   "_NET_WM_USER_TIME",
+  "_NET_WM_MOUSE_ACTION",
 };
 
 GType
@@ -253,6 +254,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
   {
Index: gdk/x11/gdkdisplay-x11.h
===================================================================
RCS file: /cvs/gnome/gtk+/gdk/x11/gdkdisplay-x11.h,v
retrieving revision 1.18
diff -p -u -r1.18 gdkdisplay-x11.h
--- gdk/x11/gdkdisplay-x11.h	11 Jul 2004 13:26:57 -0000	1.18
+++ gdk/x11/gdkdisplay-x11.h	12 Oct 2004 19:30:15 -0000
@@ -143,6 +143,10 @@ 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;
+  guint mouse_action_motion_occurred : 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.223
diff -p -u -r1.223 gdkwindow-x11.c
--- gdk/x11/gdkwindow-x11.c	5 Oct 2004 05:40:42 -0000	1.223
+++ gdk/x11/gdkwindow-x11.c	12 Oct 2004 19:30:16 -0000
@@ -474,12 +474,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)
@@ -3736,6 +3737,168 @@ 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);
+
+  display_x11->mouse_action_begin_time = timestamp;
+  send_mouse_action_message (window, display, action, timestamp, TRUE);
+  display_x11->mouse_action_motion_occurred = FALSE;
+}
+
+/**
+ * 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);
+
+  send_mouse_action_message (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;
 }
 
 /**
Index: gtk/gtkdnd.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkdnd.c,v
retrieving revision 1.117
diff -p -u -r1.117 gtkdnd.c
--- gtk/gtkdnd.c	9 Aug 2004 16:59:51 -0000	1.117
+++ gtk/gtkdnd.c	12 Oct 2004 19:30:16 -0000
@@ -1873,6 +1873,8 @@ gtk_drag_begin_internal (GtkWidget      
   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));
   
   gtk_drag_get_event_actions (event, button, actions,
Index: gtk/gtkentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkentry.c,v
retrieving revision 1.265
diff -p -u -r1.265 gtkentry.c
--- gtk/gtkentry.c	8 Oct 2004 04:24:45 -0000	1.265
+++ gtk/gtkentry.c	12 Oct 2004 19:30:16 -0000
@@ -1507,6 +1507,10 @@ gtk_entry_button_press (GtkWidget      *
       entry->select_words = FALSE;
       entry->select_lines = FALSE;
 
+      gdk_window_begin_mouse_action (gdk_window_get_toplevel (widget->window),
+                                     GDK_MOUSE_ACTION_SELECTION,
+                                     event->time);
+
       if (event->state & GDK_SHIFT_MASK)
 	{
 	  gtk_entry_reset_im_context (entry);
@@ -1632,6 +1636,10 @@ gtk_entry_button_release (GtkWidget     
   if (event->window != entry->text_area || entry->button != event->button)
     return FALSE;
 
+  gdk_window_end_mouse_action (gdk_window_get_toplevel (widget->window),
+                               GDK_MOUSE_ACTION_SELECTION,
+                               event->time);
+
   if (entry->in_drag)
     {
       gint tmp_pos = gtk_entry_find_position (entry, entry->drag_start_x);
@@ -1668,6 +1676,8 @@ gtk_entry_motion_notify (GtkWidget      
   if (event->window != entry->text_area || entry->button != 1)
     return FALSE;
 
+  gdk_window_mouse_motion_occurred (gdk_window_get_toplevel (widget->window));
+      
   if (entry->select_lines)
     return TRUE;
 
Index: gtk/gtkrange.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkrange.c,v
retrieving revision 1.102
diff -p -u -r1.102 gtkrange.c
--- gtk/gtkrange.c	6 Oct 2004 05:11:36 -0000	1.102
+++ gtk/gtkrange.c	12 Oct 2004 19:30:16 -0000
@@ -1225,6 +1225,10 @@ gtk_range_button_press (GtkWidget      *
   if (range->layout->grab_location != MOUSE_OUTSIDE)
     return FALSE;
 
+  gdk_window_begin_mouse_action (gdk_window_get_toplevel (widget->window),
+                                 GDK_MOUSE_ACTION_SLIDING,
+                                 event->time);
+
   range->layout->mouse_x = event->x;
   range->layout->mouse_y = event->y;
   if (gtk_range_update_mouse_location (range))
@@ -1396,7 +1400,12 @@ gtk_range_button_release (GtkWidget     
   if (range->layout->grab_button == event->button)
     {
       if (range->layout->grab_location == MOUSE_SLIDER)
-        update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
+        {
+          gdk_window_end_mouse_action (gdk_window_get_toplevel (widget->window),
+                                       GDK_MOUSE_ACTION_SLIDING,
+                                       event->time);
+          update_slider_position (range, range->layout->mouse_x, range->layout->mouse_y);
+        }
 
       stop_scrolling (range);
       
@@ -1486,7 +1495,10 @@ gtk_range_motion_notify (GtkWidget      
     gtk_widget_queue_draw (widget);
 
   if (range->layout->grab_location == MOUSE_SLIDER)
-    update_slider_position (range, x, y);
+    {
+      gdk_window_mouse_motion_occurred (gdk_window_get_toplevel (widget->window));
+      update_slider_position (range, x, y);
+    }
 
   /* We handled the event if the mouse was in the range_rect */
   return range->layout->mouse_location != MOUSE_OUTSIDE;
Index: gtk/gtktextview.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktextview.c,v
retrieving revision 1.260
diff -p -u -r1.260 gtktextview.c
--- gtk/gtktextview.c	30 Aug 2004 13:16:39 -0000	1.260
+++ gtk/gtktextview.c	12 Oct 2004 19:30:16 -0000
@@ -5533,6 +5533,8 @@ selection_motion_event_handler (GtkTextV
 {
   SelectionGranularity granularity = GPOINTER_TO_INT (data);
 
+  gdk_window_mouse_motion_occurred (gdk_window_get_toplevel (GTK_WIDGET (text_view)->window));
+
   if (granularity == SELECT_CHARACTERS) 
     {
       move_mark_to_pointer_and_scroll (text_view, "insert");
@@ -5630,17 +5632,21 @@ selection_motion_event_handler (GtkTextV
 static void
 gtk_text_view_start_selection_drag (GtkTextView       *text_view,
                                     const GtkTextIter *iter,
-                                    GdkEventButton    *button)
+                                    GdkEventButton    *event)
 {
   GtkTextIter start, end;
   GtkTextBuffer *buffer;
   SelectionGranularity granularity;
 
+  gdk_window_begin_mouse_action (gdk_window_get_toplevel (GTK_WIDGET (text_view)->window),
+                                 GDK_MOUSE_ACTION_SELECTION,
+                                 event->time);
+
   g_return_if_fail (text_view->selection_drag_handler == 0);
 
-  if (button->type == GDK_2BUTTON_PRESS)
+  if (event->type == GDK_2BUTTON_PRESS)
     granularity = SELECT_WORDS;
-  else if (button->type == GDK_3BUTTON_PRESS)
+  else if (event->type == GDK_3BUTTON_PRESS)
     granularity = SELECT_LINES;
   else 
     granularity = SELECT_CHARACTERS;
@@ -5653,7 +5659,7 @@ gtk_text_view_start_selection_drag (GtkT
   
   extend_selection (text_view, granularity, &start, &end);
 
-  if (button->state & GDK_SHIFT_MASK)
+  if (event->state & GDK_SHIFT_MASK)
     {
       /* Extend selection */
       GtkTextIter old_start, old_end;
@@ -5682,6 +5688,10 @@ gtk_text_view_end_selection_drag (GtkTex
 {
   if (text_view->selection_drag_handler == 0)
     return FALSE;
+
+  gdk_window_end_mouse_action (gdk_window_get_toplevel (GTK_WIDGET (text_view)->window),
+                               GDK_MOUSE_ACTION_SELECTION,
+                               event->time);
 
   g_signal_handler_disconnect (text_view, text_view->selection_drag_handler);
   text_view->selection_drag_handler = 0;
