Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.214
diff -u -r1.214 display.c
--- src/display.c	23 Feb 2004 01:48:29 -0000	1.214
+++ src/display.c	14 Apr 2004 17:54:35 -0000
@@ -281,6 +281,7 @@
     "_NET_WM_ACTION_MINIMIZE",
     "_NET_FRAME_EXTENTS",
     "_NET_REQUEST_FRAME_EXTENTS",
+    "_NET_WM_USER_TIME",
   };
   Atom atoms[G_N_ELEMENTS(atom_names)];
   
@@ -427,6 +428,7 @@
   display->atom_net_wm_action_minimize = atoms[82];
   display->atom_net_frame_extents = atoms[83];
   display->atom_net_request_frame_extents = atoms[84];
+  display->atom_net_wm_user_time = atoms[85];
 
   display->prop_hooks = NULL;
   meta_display_init_window_prop_hooks (display);
@@ -1177,7 +1179,7 @@
   Window modified;
   gboolean frame_was_receiver;
   gboolean filter_out_event;
-  
+
   display = data;
   
   if (dump_events)
@@ -1326,6 +1328,17 @@
         }
     }
 #endif /* HAVE_SHAPE */
+
+  if (window &&
+      ((event->type == KeyPress) ||
+       (event->type == ButtonPress)) &&
+      (CurrentTime != display->current_time))
+    {
+      meta_warning ("Metacity set %s's net_wm_user_time to %d.\n",
+                    window->desc, display->current_time);
+      window->net_wm_user_time_set = TRUE;
+      window->net_wm_user_time = display->current_time;
+    }
   
   switch (event->type)
     {
Index: src/display.h
===================================================================
RCS file: /cvs/gnome/metacity/src/display.h,v
retrieving revision 1.116
diff -u -r1.116 display.h
--- src/display.h	23 Feb 2004 01:48:29 -0000	1.116
+++ src/display.h	14 Apr 2004 17:54:35 -0000
@@ -175,6 +175,7 @@
   Atom atom_net_wm_strut_partial;
   Atom atom_net_frame_extents;
   Atom atom_net_request_frame_extents;
+  Atom atom_net_wm_user_time;
 
   /* This is the actual window from focus events,
    * not the one we last set
Index: src/screen.c
===================================================================
RCS file: /cvs/gnome/metacity/src/screen.c,v
retrieving revision 1.123
diff -u -r1.123 screen.c
--- src/screen.c	21 Jan 2004 17:33:56 -0000	1.123
+++ src/screen.c	14 Apr 2004 17:54:35 -0000
@@ -2481,6 +2481,7 @@
   if (sequence != NULL)
     {
       int space;
+      Time timestamp;
           
       meta_topic (META_DEBUG_STARTUP,
                   "Found startup sequence for window %s ID \"%s\"\n",
@@ -2497,6 +2498,20 @@
               
               window->initial_workspace_set = TRUE;
               window->initial_workspace = space;
+            }
+        }
+
+      if (!window->initial_timestamp_set)
+        {
+          if (sn_startup_sequence_get_timestamp_set (sequence))
+            {
+              timestamp = sn_startup_sequence_get_timestamp (sequence);
+              meta_topic (META_DEBUG_STARTUP,
+                          "Setting initial window timestamp to %lu based on startup info\n",
+                          timestamp);
+              
+              window->initial_timestamp_set = TRUE;
+              window->initial_timestamp = timestamp;
             }
         }
 
Index: src/stack.c
===================================================================
RCS file: /cvs/gnome/metacity/src/stack.c,v
retrieving revision 1.70
diff -u -r1.70 stack.c
--- src/stack.c	2 Mar 2004 02:28:21 -0000	1.70
+++ src/stack.c	14 Apr 2004 17:54:35 -0000
@@ -1517,3 +1517,43 @@
               "Window %s had stack_position set to %d\n",
               window->desc, window->stack_position);
 }
+
+void
+meta_window_place_window_below_other_in_stack (MetaWindow *window,
+                                               MetaWindow *below_this_one)
+{
+  g_return_if_fail (window         != NULL);
+  g_return_if_fail (below_this_one != NULL);
+  g_return_if_fail (WINDOW_IN_STACK (window));
+  g_return_if_fail (WINDOW_IN_STACK (below_this_one));
+
+  if (window->stack_position > below_this_one->stack_position)
+    {
+      /*
+      meta_topic (META_DEBUG_STACK,
+                  "Setting stack position of window %s to %d (making it below window %s).\n",
+                  window->desc, 
+                  below_this_one->stack_position-1, 
+                  below_this_one->desc);
+      */
+      meta_warning (
+                  "Setting stack position of window %s to %d (making it below window %s).\n",
+                  window->desc, 
+                  below_this_one->stack_position-1, 
+                  below_this_one->desc);
+      meta_window_set_stack_position (window, below_this_one->stack_position-1);
+    }
+  else
+    {
+      /*
+      meta_topic (META_DEBUG_STACK,
+                  "Window %s  was already below window %s.\n",
+                  window->desc, below_this_one->desc);
+      */
+      meta_warning (
+                  "Window %s  was already below window %s.\n",
+                  window->desc, below_this_one->desc);
+    }
+
+  meta_stack_sync_to_server (window->screen->stack);
+}
Index: src/stack.h
===================================================================
RCS file: /cvs/gnome/metacity/src/stack.h,v
retrieving revision 1.16
diff -u -r1.16 stack.h
--- src/stack.h	22 Jan 2003 04:54:03 -0000	1.16
+++ src/stack.h	14 Apr 2004 17:54:35 -0000
@@ -139,9 +139,7 @@
 
 void meta_window_set_stack_position (MetaWindow *window,
                                      int         position);
+void meta_window_place_window_below_other_in_stack (MetaWindow *window,
+                                                    MetaWindow *below_this_one);
 
 #endif
-
-
-
-
Index: src/window-props.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window-props.c,v
retrieving revision 1.15
diff -u -r1.15 window-props.c
--- src/window-props.c	13 Dec 2003 23:00:06 -0000	1.15
+++ src/window-props.c	14 Apr 2004 17:54:35 -0000
@@ -171,6 +171,30 @@
 }
 
 static void
+init_net_wm_user_time (MetaDisplay   *display,
+                       Atom           property,
+                       MetaPropValue *value)
+{
+  value->type = META_PROP_VALUE_CARDINAL;
+  value->atom = display->atom_net_wm_user_time;
+}
+
+static void
+reload_net_wm_user_time (MetaWindow    *window,
+                         MetaPropValue *value)
+{
+  if (value->type != META_PROP_VALUE_INVALID)
+    {
+      gulong cardinal = value->v.cardinal;
+
+      window->net_wm_user_time_set = TRUE;
+      window->net_wm_user_time = cardinal;
+      meta_warning ("Window %s has _NET_WM_USER_TIME of %lu\n",
+                    window->desc, window->net_wm_user_time);
+    }
+}
+
+static void
 set_window_title (MetaWindow *window,
                   const char *title)
 {
@@ -814,7 +838,7 @@
 
 
 
-#define N_HOOKS 23
+#define N_HOOKS 24
 
 void
 meta_display_init_window_prop_hooks (MetaDisplay *display)
@@ -842,6 +866,11 @@
   hooks[i].property = display->atom_net_wm_pid;
   hooks[i].init_func = init_net_wm_pid;
   hooks[i].reload_func = reload_net_wm_pid;
+  ++i;
+
+  hooks[i].property = display->atom_net_wm_user_time;
+  hooks[i].init_func = init_net_wm_user_time;
+  hooks[i].reload_func = reload_net_wm_user_time;
   ++i;
 
   hooks[i].property = display->atom_net_wm_name;
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.325
diff -u -r1.325 window.c
--- src/window.c	28 Feb 2004 02:53:55 -0000	1.325
+++ src/window.c	14 Apr 2004 17:54:36 -0000
@@ -47,6 +47,14 @@
 #include <X11/extensions/shape.h>
 #endif
 
+/* Xserver time can wraparound, thus comparing two timestamps needs to take
+ * this into account.  Here's a little macro to help out.
+ */
+#define XSERVER_TIME_IS_LATER(time1,time2)                  \
+  ( ((time1 >= time2) && (time1-time2 < G_MAXULONG / 2)) ||  \
+    ((time1 <  time2) && (time2-time1 > G_MAXULONG / 2))     \
+  )
+
 typedef enum
 {
   META_IS_CONFIGURE_REQUEST = 1 << 0,
@@ -216,7 +224,7 @@
   MetaWorkspace *space;
   gulong existing_wm_state;
   gulong event_mask;
-#define N_INITIAL_PROPS 12
+#define N_INITIAL_PROPS 13
   Atom initial_props[N_INITIAL_PROPS];
   int i;
   gboolean has_shape;
@@ -443,6 +451,8 @@
   window->all_keys_grabbed = FALSE;
   window->withdrawn = FALSE;
   window->initial_workspace_set = FALSE;
+  window->initial_timestamp_set = FALSE;
+  window->net_wm_user_time_set = FALSE;
   window->calc_placement = FALSE;
   window->shaken_loose = FALSE;
   window->have_focus_click_grab = FALSE;
@@ -505,6 +515,7 @@
   window->layer = META_LAYER_LAST; /* invalid value */
   window->stack_position = -1;
   window->initial_workspace = 0; /* not used */
+  window->initial_timestamp = 0; /* not used */
   
   meta_display_register_x_window (display, &window->xwindow, window);
 
@@ -532,6 +543,7 @@
   initial_props[i++] = XA_WM_NORMAL_HINTS;
   initial_props[i++] = display->atom_wm_protocols;
   initial_props[i++] = XA_WM_HINTS;
+  initial_props[i++] = display->atom_net_wm_user_time;
   g_assert (N_INITIAL_PROPS == i);
   
   meta_window_reload_properties (window, initial_props, N_INITIAL_PROPS);
@@ -1568,41 +1580,67 @@
       /* don't focus these */
       break;
     case META_WINDOW_NORMAL:
-
-      /* Always focus new windows */
-      return TRUE;
-
-      /* Old Windows-XP style rule for reference */
-      /* Focus only if the current focus is on a desktop element
-       * or nonexistent.
-       *
-       * (using display->focus_window is a bit of a race condition,
-       *  but I have no idea how to avoid it)
-       */
-      if (window->display->focus_window == NULL ||
-          (window->display->focus_window &&
-           (window->display->focus_window->type == META_WINDOW_DOCK ||
-            window->display->focus_window->type == META_WINDOW_DESKTOP)))
-        return TRUE;
-      break;
     case META_WINDOW_DIALOG:
     case META_WINDOW_MODAL_DIALOG:
 
-      /* Always focus */
-      return TRUE;
+      meta_warning ("COMPARISON:\n");
+      meta_warning ("  net_wm_user_time_set : %d\n",  window->net_wm_user_time_set);
+      meta_warning ("  net_wm_user_time     : %lu\n", window->net_wm_user_time);
+      meta_warning ("  initial_timestamp_set: %d\n",  window->initial_timestamp_set);
+      meta_warning ("  initial_timestamp    : %lu\n", window->initial_timestamp);
+      if (window->display->focus_window != NULL) {
+      meta_warning ("  focus_window         : %s\n",  window->display->focus_window->desc);
+      meta_warning ("  fw->net_wm_user_time : %lu\n", window->display->focus_window->net_wm_user_time);
+      }
+
+      /* We expect the most common case for not focusing a new window
+         to be when a hint to not focus it has been set.  Since we can
+         deal with that case rapidly, we use special case it--this is
+         merely a preliminary optimization.  :)  */
+      if ( ((window->net_wm_user_time_set == TRUE) &&
+	   (window->net_wm_user_time == 0))
+          ||
+           ((window->initial_timestamp_set == TRUE) &&
+	   (window->initial_timestamp == 0)))
+        {
+          meta_warning ("STARTUP--window %s explicitly requested no focus\n",
+                        window->desc);
+          return FALSE;
+        }
+
+      if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set))
+        {
+          meta_warning ("STARTUP--no information about window %s found\n",
+                        window->desc);
+          return TRUE;
+        }
+
+      /* To determine the "launch" time of an application,
+       * startup-notification can set the TIMESTAMP and the
+       * application (usually via its toolkit such as gtk or qt) can
+       * set the _NET_WM_USER_TIME.  If both are set, then it means
+       * the user has interacted with the application since it
+       * launched, and _NET_WM_USER_TIME is the value that should be
+       * used in the comparison.
+       **/
+      Time compare;
+      compare = window->initial_timestamp_set ? window->initial_timestamp : 0;
+      compare = window->net_wm_user_time_set  ? window->net_wm_user_time  : compare;
+
+      if ((window->display->focus_window == NULL) ||
+          (XSERVER_TIME_IS_LATER(compare,window->display->focus_window->net_wm_user_time)))
+        {
+          meta_warning ("STARTUP--new window %s with no intervening events\n",
+                        window->desc);
+          return TRUE;
+        }
+      else
+        {
+          meta_warning ("STARTUP--window %s focus prevented by other activity; %lu is before %lu\n",
+                        window->desc, compare, window->display->focus_window->net_wm_user_time);
+          return FALSE;
+        }
 
-      /* Old Windows-XP style rule for reference */
-      /* Focus only if the transient parent has focus */
-      /* (using display->focus_window is a bit of a race condition,
-       *  but I have no idea how to avoid it)
-       */
-      if (window->display->focus_window == NULL ||
-          (window->display->focus_window &&
-           meta_window_is_ancestor_of_transient (window->display->focus_window,
-                                                 window)) ||
-          (window->display->focus_window->type == META_WINDOW_DOCK ||
-           window->display->focus_window->type == META_WINDOW_DESKTOP))
-        return TRUE;
       break;
     }
 
@@ -1614,13 +1652,25 @@
 {
   gboolean did_placement;
   gboolean did_show;
-  
+  gboolean takes_focus_on_map;
+
+  /*  
   meta_topic (META_DEBUG_WINDOW_STATE,
               "Showing window %s, shaded: %d iconic: %d placed: %d\n",
               window->desc, window->shaded, window->iconic, window->placed);
+  */
+  meta_warning (
+              "Showing window %s, shaded: %d iconic: %d placed: %d\n",
+              window->desc, window->shaded, window->iconic, window->placed);
 
   did_show = FALSE;
   did_placement = FALSE;
+  takes_focus_on_map = window_takes_focus_on_map (window);
+
+  if ( (!takes_focus_on_map) && (window->display->focus_window != NULL) )
+    meta_window_place_window_below_other_in_stack (window,
+                                                   window->display->focus_window);
+
   if (!window->placed)
     {
       /* We have to recalc the placement here since other windows may
@@ -1717,7 +1767,7 @@
             }
         }
 
-      if (window_takes_focus_on_map (window))
+      if (takes_focus_on_map)
         {                
           meta_window_focus (window,
                              meta_display_get_current_time (window->display));
@@ -4370,6 +4420,13 @@
       
       meta_window_reload_property (window,
                                    window->display->atom_metacity_update_counter);
+    }
+  else if (event->atom == window->display->atom_net_wm_user_time)
+    {
+      meta_warning ("Property notify on %s for _NET_WM_USER_TIME\n", window->desc);
+      
+      meta_window_reload_property (window,
+                                   window->display->atom_net_wm_user_time);
     }
 
   return TRUE;
Index: src/window.h
===================================================================
RCS file: /cvs/gnome/metacity/src/window.h,v
retrieving revision 1.101
diff -u -r1.101 window.h
--- src/window.h	28 Feb 2004 23:58:40 -0000	1.101
+++ src/window.h	14 Apr 2004 17:54:36 -0000
@@ -103,6 +103,9 @@
   /* Initial workspace property */
   int initial_workspace;  
   
+  /* Initial timestamp property */
+  Time initial_timestamp;  
+  
   /* Whether we're maximized */
   guint maximized : 1;
   guint maximize_after_placement : 1;
@@ -139,6 +142,12 @@
   /* whether an initial workspace was explicitly set */
   guint initial_workspace_set : 1;
   
+  /* whether an initial timestamp was explicitly set */
+  guint initial_timestamp_set : 1;
+  
+  /* whether net_wm_user_time has been set yet */
+  guint net_wm_user_time_set : 1;
+  
   /* These are the flags from WM_PROTOCOLS */
   guint take_focus : 1;
   guint delete_window : 1;
@@ -243,6 +252,10 @@
 
   /* if TRUE we have a grab on the focus click buttons */
   guint have_focus_click_grab : 1;
+
+  /* set to the most recent user-interaction event timestamp that we
+     know about for this window */
+  Time net_wm_user_time;
   
 #ifdef HAVE_XSYNC
   /* XSync update counter */
