Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.238
diff -p -u -r1.238 display.c
--- src/display.c	20 Dec 2004 02:46:42 -0000	1.238
+++ src/display.c	22 Dec 2004 23:29:19 -0000
@@ -632,6 +632,7 @@ meta_display_open (const char *name)
     timestamp = event.xproperty.time;
   }
 
+  display->last_focus_time = timestamp;
   display->compositor = meta_compositor_new (display);
   
   screens = NULL;
@@ -676,20 +677,28 @@ meta_display_open (const char *name)
     /* kinda bogus because GetInputFocus has no possible errors */
     meta_error_trap_push (display);
 
+    /* FIXME: This is totally broken; see comment 9 of bug 88194 about this */
     focus = None;
     ret_to = RevertToPointerRoot;
     XGetInputFocus (display->xdisplay, &focus, &ret_to);
 
     /* Force a new FocusIn (does this work?) */
-    if (focus == None || focus == PointerRoot)
-      focus = display->no_focus_window;
 
     /* Use the same timestamp that was passed to meta_screen_new(),
      * as it is the most recent timestamp.
      */
-    XSetInputFocus (display->xdisplay, focus, RevertToPointerRoot,
-                    timestamp);
-    
+    if (focus == None || focus == PointerRoot)
+      meta_display_focus_the_no_focus_window (display, timestamp);
+    else
+      {
+        MetaWindow * window;
+        window  = meta_display_lookup_x_window (display, focus);
+        if (window)
+          meta_display_set_input_focus_window (display, window, FALSE, timestamp);
+        else
+          meta_display_focus_the_no_focus_window (display, timestamp);
+      }
+
     meta_error_trap_pop (display, FALSE);
   }
   
@@ -4598,14 +4607,39 @@ meta_display_focus_sentinel_clear (MetaD
 }
 
 void
+meta_display_set_input_focus_window (MetaDisplay *display, 
+                                     MetaWindow  *window,
+                                     gboolean     focus_frame,
+                                     Time         timestamp)
+{
+  if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time))
+    return;
+
+  XSetInputFocus (display->xdisplay,
+                  focus_frame ? window->frame->xwindow : window->xwindow,
+                  RevertToPointerRoot,
+                  timestamp);
+  display->expected_focus_window = window;
+  display->last_focus_time = timestamp;
+
+  if (window != display->autoraise_window)
+    meta_display_remove_autoraise_callback (window->display);
+}
+
+void
 meta_display_focus_the_no_focus_window (MetaDisplay *display, 
                                         Time         timestamp)
 {
+  if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time))
+    return;
+
   XSetInputFocus (display->xdisplay,
                   display->no_focus_window,
                   RevertToPointerRoot,
                   timestamp);
   display->expected_focus_window = NULL;
+  display->last_focus_time = timestamp;
+
   meta_display_remove_autoraise_callback (display);
 }
 
Index: src/display.h
===================================================================
RCS file: /cvs/gnome/metacity/src/display.h,v
retrieving revision 1.125
diff -p -u -r1.125 display.h
--- src/display.h	4 Oct 2004 21:09:08 -0000	1.125
+++ src/display.h	22 Dec 2004 23:29:19 -0000
@@ -192,10 +192,18 @@ struct _MetaDisplay
   */
   MetaWindow *previously_focused_window;
 
-  /* window we are expecting a FocusIn event for
+  /* window we are expecting a FocusIn event for or the current focus
+   * window if we are not expecting any FocusIn/FocusOut events; not
+   * perfect because applications can call XSetInputFocus directly.
+   * (It could also be messed up if a timestamp later than current
+   * time is sent to meta_display_set_input_focus_window, though that
+   * would be a programming error).  See bug 154598 for more info.
    */
   MetaWindow *expected_focus_window;
 
+  /* last timestamp that a window was focused */
+  Time last_focus_time;
+
   guint static_gravity_works : 1;
   
   /*< private-ish >*/
@@ -350,6 +358,18 @@ struct _MetaDisplay
 #endif
 };
 
+/* Xserver time can wraparound, thus comparing two timestamps needs to take
+ * this into account.  Here's a little macro to help out.  If no wraparound
+ * 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.
+ */
+#define XSERVER_TIME_IS_BEFORE(time1, time2)                       \
+  ( (( time1 < time2 ) && ( time2 - time1 < ((guint32)-1)/2 )) ||  \
+    (( time1 > time2 ) && ( time1 - time2 > ((guint32)-1)/2 ))     \
+  )
+
 gboolean      meta_display_open                (const char  *name);
 void          meta_display_close               (MetaDisplay *display);
 MetaScreen*   meta_display_screen_for_root     (MetaDisplay *display,
@@ -486,8 +506,25 @@ void meta_display_increment_focus_sentin
 void meta_display_decrement_focus_sentinel (MetaDisplay *display);
 gboolean meta_display_focus_sentinel_clear (MetaDisplay *display);
 
+/* meta_display_set_input_focus_window is like XSetInputFocus, except
+ * that (a) it can't detect timestamps later than the current time,
+ * since Metacity isn't part of the XServer, and thus gives erroneous
+ * behavior in this circumstance (so don't do it), and (b) it uses
+ * display->last_focus_time and display->expected_focus_window since
+ * we don't have access to the true Xserver ones.
+ */
+void meta_display_set_input_focus_window   (MetaDisplay *display, 
+                                            MetaWindow  *window,
+                                            gboolean     focus_frame,
+                                            Time         timestamp);
+
+/* meta_display_focus_the_no_focus_window is called when the
+ * designated no_focus_window should be focused, but is otherwise the
+ * same as meta_display_set_input_focus_window
+ */
 void meta_display_focus_the_no_focus_window (MetaDisplay *display, 
                                              Time         timestamp);
+
 void meta_display_queue_autoraise_callback  (MetaDisplay *display,
                                              MetaWindow  *window);
 void meta_display_remove_autoraise_callback (MetaDisplay *display);
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.353
diff -p -u -r1.353 window.c
--- src/window.c	20 Dec 2004 02:53:08 -0000	1.353
+++ src/window.c	22 Dec 2004 23:29:19 -0000
@@ -47,14 +47,6 @@
 #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,
@@ -1666,7 +1658,7 @@ window_takes_focus_on_map (MetaWindow *w
       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)))
+          XSERVER_TIME_IS_BEFORE (window->display->focus_window->net_wm_user_time, compare))
         {
           meta_topic (META_DEBUG_STARTUP,
                       "new window %s with no intervening events\n",
@@ -3317,11 +3309,10 @@ meta_window_focus (MetaWindow  *window,
         {
           meta_topic (META_DEBUG_FOCUS,
                       "Focusing frame of %s\n", window->desc);
-          XSetInputFocus (window->display->xdisplay,
-                          window->frame->xwindow,
-                          RevertToPointerRoot,
-                          timestamp);
-          window->display->expected_focus_window = window;
+          meta_display_set_input_focus_window (window->display,
+                                               window,
+                                               TRUE,
+                                               timestamp);
         }
     }
   else
@@ -3331,13 +3322,12 @@ meta_window_focus (MetaWindow  *window,
       if (window->input)
         {
           meta_topic (META_DEBUG_FOCUS,
-                      "Calling XSetInputFocus() on client window %s since input = true\n",
+                      "Setting input focus on %s since input = true\n",
                       window->desc);
-          XSetInputFocus (window->display->xdisplay,
-                          window->xwindow,
-                          RevertToPointerRoot,
-                          timestamp);
-          window->display->expected_focus_window = window;
+          meta_display_set_input_focus_window (window->display,
+                                               window,
+                                               FALSE,
+                                               timestamp);
         }
       
       if (window->take_focus)
@@ -3359,10 +3349,6 @@ meta_window_focus (MetaWindow  *window,
       window->wm_state_demands_attention = FALSE;
       set_net_wm_state (window);
     }  
-
-  /* Check if there's an autoraise timeout for a different window */
-  if (window != window->display->autoraise_window)
-    meta_display_remove_autoraise_callback (window->display);
 }
 
 static void
