Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.251
diff -p -u -r1.251 display.c
--- src/display.c	16 Feb 2005 09:36:24 -0000	1.251
+++ src/display.c	19 Feb 2005 20:12:52 -0000
@@ -115,6 +115,10 @@ static void    update_window_grab_modifi
 static void    prefs_changed_callback    (MetaPreference pref,
                                           void          *data);
 
+static void    sanity_check_timestamps   (MetaDisplay *display,
+                                          Time         timestamp,
+                                          guint32      fudge_factor);
+
 static void
 set_utf8_string_hint (MetaDisplay *display,
                       Window xwindow,
@@ -645,6 +649,7 @@ meta_display_open (const char *name)
   }
 
   display->last_focus_time = timestamp;
+  display->last_user_time = timestamp;
   display->compositor = meta_compositor_new (display);
   
   screens = NULL;
@@ -1146,6 +1151,8 @@ meta_display_get_current_time_roundtrip 
       timestamp = property_event.xproperty.time;
     }
 
+  sanity_check_timestamps (display, timestamp, 0);
+
   return timestamp;
 }
 
@@ -1563,11 +1570,8 @@ event_callback (XEvent   *event,
   if (window && ((event->type == KeyPress) || (event->type == ButtonPress)))
     {
       g_assert (CurrentTime != display->current_time);
-      meta_topic (META_DEBUG_WINDOW_STATE,
-                  "Metacity set %s's net_wm_user_time to %lu.\n",
-                  window->desc, display->current_time);
-      window->net_wm_user_time_set = TRUE;
-      window->net_wm_user_time = display->current_time;
+      meta_window_set_user_time (window, display->current_time);
+      sanity_check_timestamps (display, display->current_time, 5000);
     }
   
   switch (event->type)
@@ -4648,21 +4652,122 @@ meta_display_focus_sentinel_clear (MetaD
   return (display->sentinel_counter == 0);
 }
 
+static void
+sanity_check_timestamps (MetaDisplay *display,
+                         Time         timestamp,
+                         guint32      fudge_factor)
+{
+  /* we use volatile to ensure we get guint32 wraparound instead of some higher
+   * precision nonsense that the compiler might want to use...
+   */
+  volatile guint32 compare;
+
+  compare = timestamp + fudge_factor;
+
+  if (XSERVER_TIME_IS_BEFORE (compare, display->last_focus_time))
+    {
+      meta_warning ("last_focus_time (%lu) is greater than comparison "
+                    "timestamp (%lu).  This most likely represents a buggy "
+                    "client sending inaccurate timestamps in messages such as "
+                    "_NET_ACTIVE_WINDOW.  Trying to work around...\n",
+                    display->last_focus_time, (unsigned long)compare);
+      display->last_focus_time = timestamp;
+    }
+  if (XSERVER_TIME_IS_BEFORE (compare, display->last_user_time))
+    {
+      GSList *windows;
+      GSList *tmp;
+
+      meta_warning ("last_user_time (%lu) is greater than comparison "
+                    "timestamp (%lu).  This most likely represents a buggy "
+                    "client sending inaccurate timestamps in messages such as "
+                    "_NET_ACTIVE_WINDOW.  Trying to work around...\n",
+                    display->last_user_time, (unsigned long)compare);
+      display->last_user_time = timestamp;
+
+      windows = meta_display_list_windows (display);
+      tmp = windows;
+      while (tmp != NULL)
+        {
+          MetaWindow *window = tmp->data;
+          
+          if (XSERVER_TIME_IS_BEFORE (compare, window->net_wm_user_time))
+            {
+              meta_warning ("%s appears to be one of the offending windows "
+                            "with a timestamp of %lu.  Working around...\n",
+                            window->desc, window->net_wm_user_time);
+              window->net_wm_user_time = timestamp;
+            }
+          
+          tmp = tmp->next;
+        }
+
+      g_slist_free (windows);
+    }
+}
+
+static gboolean
+timestamp_too_old (MetaDisplay *display,
+                   MetaWindow  *window,
+                   Time        *timestamp)
+{
+  /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
+   * us to sanity check the timestamp here and ensure it doesn't correspond to
+   * a future time (though we would want to rename to 
+   * timestamp_too_old_or_in_future).
+   */
+
+  MetaWindow *focus_window;
+  focus_window = display->focus_window;
+
+  if (*timestamp == CurrentTime)
+    {
+      meta_warning ("Got a request to focus %s with a timestamp of 0.  This "
+                    "shouldn't happen!\n",
+                    window ? window->desc : "the no_focus_window");
+      meta_print_backtrace ();
+      *timestamp = meta_display_get_current_time_roundtrip (display);
+      return FALSE;
+    }
+  else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
+    {
+      if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Ignoring focus request for %s since %lu "
+                      "is less than %lu and %lu.\n",
+                      window ? window->desc : "the no_focus_window",
+                      *timestamp,
+                      (unsigned long) display->last_user_time,
+                      (unsigned long) display->last_focus_time);
+          return TRUE;
+        }
+      else
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Received focus request for %s which is newer than most "
+                      "recent user_time, but less recent than "
+                      "last_focus_time (%lu < %lu < %lu); adjusting "
+                      "accordingly.  (See bug 167358)\n",
+                      window ? window->desc : "the no_focus_window",
+                      display->last_user_time,
+                      *timestamp,
+                      display->last_focus_time);
+          *timestamp = display->last_focus_time;
+          return FALSE;
+        }
+    }
+
+  return FALSE;
+}
+
 void
 meta_display_set_input_focus_window (MetaDisplay *display, 
                                      MetaWindow  *window,
                                      gboolean     focus_frame,
                                      Time         timestamp)
 {
-  if (timestamp == CurrentTime)
-    {
-      meta_warning ("meta_display_set_input_focus_window called with a "
-                    "timestamp of 0 for window %s.  This shouldn't happen!\n",
-                    window->desc);
-      meta_print_backtrace ();
-      timestamp = meta_display_get_current_time_roundtrip (display);
-    }
-  else if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time))
+  if (timestamp_too_old (display, window, &timestamp))
     return;
 
   XSetInputFocus (display->xdisplay,
@@ -4680,18 +4785,8 @@ void
 meta_display_focus_the_no_focus_window (MetaDisplay *display, 
                                         Time         timestamp)
 {
-  if (timestamp == CurrentTime)
-    {
-      meta_warning ("meta_display_focus_the_no_focus_window called with a "
-                    "timestamp of 0.  This shouldn't happen!\n");
-      meta_print_backtrace ();
-      timestamp = meta_display_get_current_time_roundtrip (display);
-    }
-  else if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time))
-    {
-      meta_warning ("Ignoring focus request for no_focus_window since %lu is less than %lu.\n", timestamp, display->last_focus_time);
+  if (timestamp_too_old (display, NULL, &timestamp))
     return;
-    }
 
   XSetInputFocus (display->xdisplay,
                   display->no_focus_window,
Index: src/display.h
===================================================================
RCS file: /cvs/gnome/metacity/src/display.h,v
retrieving revision 1.129
diff -p -u -r1.129 display.h
--- src/display.h	7 Feb 2005 04:37:35 -0000	1.129
+++ src/display.h	19 Feb 2005 20:12:52 -0000
@@ -201,6 +201,9 @@ struct _MetaDisplay
   /* last timestamp that a window was focused */
   Time last_focus_time;
 
+  /* last user interaction time in any app */
+  Time last_user_time;
+
   guint static_gravity_works : 1;
   
   /*< private-ish >*/
@@ -361,11 +364,21 @@ struct _MetaDisplay
  * has occurred, this is equivalent to
  *   time1 < time2
  * Of course, the rest of the ugliness of this macro comes from accounting
- * for the fact that wraparound can occur.
+ * for the fact that wraparound can occur and the fact that a timestamp of
+ * 0 must be special-cased since it means older than anything else. 
+ *
+ * Note that this is NOT an equivalent for time1 <= time2; if that's what
+ * you need then you'll need to swap the order of the arguments and negate
+ * the result.
  */
-#define XSERVER_TIME_IS_BEFORE(time1, time2)                       \
-  ( (( time1 < time2 ) && ( time2 - time1 < ((guint32)-1)/2 )) ||  \
-    (( time1 > time2 ) && ( time1 - time2 > ((guint32)-1)/2 ))     \
+#define XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) \
+  ( (( time1 < time2 ) && ( time2 - time1 < ((guint32)-1)/2 )) ||     \
+    (( time1 > time2 ) && ( time1 - time2 > ((guint32)-1)/2 ))        \
+  )
+#define XSERVER_TIME_IS_BEFORE(time1, time2)                          \
+  ( time1 == 0 ||                                                     \
+    (XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) && \
+     time2 != 0)                                                      \
   )
 
 gboolean      meta_display_open                (const char  *name);
Index: src/window-props.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window-props.c,v
retrieving revision 1.18
diff -p -u -r1.18 window-props.c
--- src/window-props.c	25 Oct 2004 16:28:57 -0000	1.18
+++ src/window-props.c	19 Feb 2005 20:12:53 -0000
@@ -186,12 +186,7 @@ reload_net_wm_user_time (MetaWindow    *
   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_topic (META_DEBUG_STARTUP,
-                  "Window %s has _NET_WM_USER_TIME of %lu\n",
-                  window->desc, window->net_wm_user_time);
+      meta_window_set_user_time (window, cardinal);
     }
 }
 
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.377
diff -p -u -r1.377 window.c
--- src/window.c	12 Feb 2005 07:34:30 -0000	1.377
+++ src/window.c	19 Feb 2005 20:12:55 -0000
@@ -2206,15 +2206,20 @@ void
 meta_window_activate (MetaWindow *window,
                       guint32     timestamp)
 {
+  meta_topic (META_DEBUG_FOCUS,
+              "_NET_ACTIVE_WINDOW message sent for %s at time %lu.\n",
+              window->desc, (unsigned long)timestamp);
+
   /* Older EWMH spec didn't specify a timestamp, so it can be 0 and we
    * have to treat that as a new request.
    */
-  if (XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_focus_time) &&
+  if (XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time) &&
       timestamp != 0)
     {
       meta_topic (META_DEBUG_FOCUS,
-                  "_NET_ACTIVE_WINDOW message sent but with a timestamp that"
-                  "requests the window not be active, so we're ignoring it.\n");
+                  "last_user_time (%lu) is more recent; ignoring "
+                  " _NET_ACTIVE_WINDOW message.\n",
+                  window->display->last_user_time);
       window->wm_state_demands_attention = TRUE;
       set_net_wm_state (window);
       return;
@@ -2223,7 +2228,7 @@ meta_window_activate (MetaWindow *window
   if (timestamp == 0)
     timestamp = meta_display_get_current_time_roundtrip (window->display);
 
-  window->net_wm_user_time = timestamp;
+  meta_window_set_user_time (window, timestamp);
 
   /* disable show desktop mode unless we're a desktop component */
   maybe_leave_show_desktop_mode (window);
@@ -4305,7 +4310,7 @@ meta_window_client_message (MetaWindow *
   else if (event->xclient.message_type ==
            display->atom_net_active_window)
     {
-      meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating",
+      meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n",
                     window->desc);
 
       if (event->xclient.data.l[0] != 0)
@@ -7376,5 +7381,36 @@ meta_window_stack_just_below (MetaWindow
       meta_topic (META_DEBUG_STACK,
                   "Window %s  was already below window %s.\n",
                   window->desc, below_this_one->desc);
+    }
+}
+
+void
+meta_window_set_user_time (MetaWindow *window,
+                           Time        timestamp)
+{
+  /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
+   * us to sanity check the timestamp here and ensure it doesn't correspond to
+   * a future time.
+   */
+
+  /* Only update the time if this timestamp is newer... */
+  if (window->net_wm_user_time_set &&
+      XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time))
+    {
+      meta_topic (META_DEBUG_STARTUP,
+                  "Window %s _NET_WM_USER_TIME not updated to %lu, because it "
+                  "is less than %lu\n",
+                  window->desc, timestamp, window->net_wm_user_time);
+
+    }
+  else
+    {
+      meta_topic (META_DEBUG_STARTUP,
+                  "Window %s has _NET_WM_USER_TIME of %lu\n",
+                  window->desc, timestamp);
+      window->net_wm_user_time_set = TRUE;
+      window->net_wm_user_time = timestamp;
+      if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp))
+        window->display->last_user_time = timestamp;
     }
 }
Index: src/window.h
===================================================================
RCS file: /cvs/gnome/metacity/src/window.h,v
retrieving revision 1.114
diff -p -u -r1.114 window.h
--- src/window.h	11 Feb 2005 19:20:44 -0000	1.114
+++ src/window.h	19 Feb 2005 20:12:55 -0000
@@ -532,4 +532,7 @@ void meta_window_queue_update_icon (Meta
 void meta_window_stack_just_below (MetaWindow *window,
                                    MetaWindow *below_this_one);
 
+void meta_window_set_user_time (MetaWindow *window,
+                                Time        timestamp);
+
 #endif
