Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/metacity/ChangeLog,v
retrieving revision 1.671
diff -p -u -r1.671 ChangeLog
--- ChangeLog	30 May 2003 04:52:10 -0000	1.671
+++ ChangeLog	30 May 2003 15:31:51 -0000
@@ -1,3 +1,37 @@
+2003-05-29  Rob Adams  <robadams@ucla.edu>
+
+	Use a new property _METACITY_SENTINEL to eliminate a race
+	condition that causes focus to behave badly with sloppy/mouse
+	focus when lots of windows are mapped/unmapped, such as with a
+	workspace switch.  The EnterNotify events on a display are ignored
+	until the PropertyNotify sent after all the window maps is
+	received.  This is a fix for #110970.
+	
+	* src/display.[ch]: New _METACITY_SENTINEL atom.
+	(event_callback): ignore EnterNotify if the sentinel isn't clear,
+	and decrement the sentinel counter when the PropertyNotify is
+	received.
+	(meta_display_increment_focus_sentinel): new function.  Increments
+	the sentinel counter and updates the property on a root window on
+	this display.
+	(meta_display_decrement_focus_sentinel): Decrement the sentinel
+	counter.
+	(meta_display_focus_sentinel_clear): returns whether the sentinel
+	counter is zero.
+
+	* src/window.c (idle_calc_showing): after showing windows, call
+	meta_display_increment_focus_sentinel on each window to be shown.
+
+	* src/workspace.[ch] (meta_workspace_activate_with_focus): new
+	function activates a workspace and focuses a particular window
+	after the workspace is activated.
+	(meta_workspace_activate): now just a wrapper for
+	meta_workspace_activate_with_focus
+
+	* src/keybindings.c: use new meta_workspace_activate_with_focus
+	function to ensure that focus will follow the focused window
+	through the workspace switch.
+
 2003-05-29  Havoc Pennington  <hp@redhat.com>
 
 	* src/theme-parser.c (meta_theme_load): s/int/gsize/ for
Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.186
diff -p -u -r1.186 display.c
--- src/display.c	30 May 2003 02:53:42 -0000	1.186
+++ src/display.c	30 May 2003 15:31:55 -0000
@@ -269,7 +269,8 @@ meta_display_open (const char *name)
     "SYNC_COUNTER",
     "_GNOME_PANEL_ACTION",
     "_GNOME_PANEL_ACTION_MAIN_MENU",
-    "_GNOME_PANEL_ACTION_RUN_DIALOG"
+    "_GNOME_PANEL_ACTION_RUN_DIALOG",
+    "_METACITY_SENTINEL"
   };
   Atom atoms[G_N_ELEMENTS(atom_names)];
   
@@ -416,6 +417,7 @@ meta_display_open (const char *name)
   display->atom_gnome_panel_action = atoms[81];
   display->atom_gnome_panel_action_main_menu = atoms[82];
   display->atom_gnome_panel_action_run_dialog = atoms[83];
+  display->atom_metacity_sentinel = atoms[84];
   
   display->prop_hooks = NULL;
   meta_display_init_window_prop_hooks (display);
@@ -473,6 +475,7 @@ meta_display_open (const char *name)
   display->ungrab_should_not_cause_focus_window = None;
   
   display->current_time = CurrentTime;
+  display->sentinel_counter = 0;
   
   display->grab_op = META_GRAB_OP_NONE;
   display->grab_window = NULL;
@@ -1434,7 +1437,8 @@ event_callback (XEvent   *event,
         meta_window_handle_mouse_grab_op_event (window, event);
       /* do this even if window->has_focus to avoid races */
       else if (window && !serial_is_ignored (display, event->xany.serial) &&
-               event->xcrossing.detail != NotifyInferior)
+               event->xcrossing.detail != NotifyInferior &&
+               meta_display_focus_sentinel_clear (display))
         {
           switch (meta_prefs_get_focus_mode ())
             {
@@ -1750,6 +1754,16 @@ event_callback (XEvent   *event,
                      display->atom_net_desktop_names)
               meta_screen_update_workspace_names (screen);
           }
+
+        /* we just use this property as a sentinel to avoid certain race
+         * conditions.  See the comment for the ignore_enter_notify
+         * variable declaration in display.h
+         */
+        if (event->xproperty.atom ==
+	    display->atom_metacity_sentinel)
+          {
+            meta_display_decrement_focus_sentinel (display);
+          }
       }
       break;
     case SelectionClear:
@@ -3995,4 +4009,42 @@ prefs_changed_callback (MetaPreference p
       MetaDisplay *display = data;
       meta_bell_set_audible (display, meta_prefs_bell_is_audible ());
     }
+}
+
+void
+meta_display_increment_focus_sentinel (MetaDisplay *display)
+{
+  unsigned long data[1];
+
+  if (meta_display_focus_sentinel_clear (display))
+    {
+      data[0] = meta_display_get_current_time (display);
+  
+      XChangeProperty (display->xdisplay,
+                       ((MetaScreen*) display->screens->data)->xroot,
+                       display->atom_metacity_sentinel,
+                       XA_CARDINAL,
+                       32, PropModeReplace, (guchar*) data, 1);
+      
+      display->sentinel_counter += 1;
+    }
+}
+
+void
+meta_display_decrement_focus_sentinel (MetaDisplay *display)
+{
+  display->sentinel_counter -= 1;
+
+  if (display->sentinel_counter < 0)
+    display->sentinel_counter = 0;
+}
+
+gboolean
+meta_display_focus_sentinel_clear (MetaDisplay *display)
+{
+  if (display->sentinel_counter == 0) {
+    return TRUE;
+  } else {
+    return FALSE;
+  }
 }
Index: src/display.h
===================================================================
RCS file: /cvs/gnome/metacity/src/display.h,v
retrieving revision 1.102
diff -p -u -r1.102 display.h
--- src/display.h	30 May 2003 02:53:42 -0000	1.102
+++ src/display.h	30 May 2003 15:31:55 -0000
@@ -172,6 +172,7 @@ struct _MetaDisplay
   Atom atom_gnome_panel_action;
   Atom atom_gnome_panel_action_main_menu;
   Atom atom_gnome_panel_action_run_dialog;
+  Atom atom_metacity_sentinel;
   
   /* This is the actual window from focus events,
    * not the one we last set
@@ -248,6 +249,12 @@ struct _MetaDisplay
   MetaResizePopup *grab_resize_popup;
   GTimeVal    grab_last_moveresize_time;
   Time        grab_motion_notify_time;
+
+  /* we use property updates as sentinels for certain window focus events
+   * to avoid some race conditions on EnterNotify events
+   */
+  int         sentinel_counter;
+
 #ifdef HAVE_XKB
   int         xkb_base_event_type;
 #endif
@@ -438,5 +445,9 @@ gboolean meta_rectangle_intersect (MetaR
 void meta_display_devirtualize_modifiers (MetaDisplay        *display,
                                           MetaVirtualModifier modifiers,
                                           unsigned int       *mask);
+
+void meta_display_increment_focus_sentinel (MetaDisplay *display);
+void meta_display_decrement_focus_sentinel (MetaDisplay *display);
+gboolean meta_display_focus_sentinel_clear (MetaDisplay *display);
 
 #endif
Index: src/keybindings.c
===================================================================
RCS file: /cvs/gnome/metacity/src/keybindings.c,v
retrieving revision 1.86
diff -p -u -r1.86 keybindings.c
--- src/keybindings.c	21 May 2003 03:45:58 -0000	1.86
+++ src/keybindings.c	30 May 2003 15:31:58 -0000
@@ -3022,7 +3022,7 @@ do_handle_move_to_workspace  (MetaDispla
       /* Activate second, so the window is never unmapped */
       meta_window_change_workspace (window, workspace);
       if (flip)
-        meta_workspace_activate (workspace);
+        meta_workspace_activate_with_focus (workspace, window);
     }
   else
     {
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.277
diff -p -u -r1.277 window.c
--- src/window.c	18 May 2003 17:16:03 -0000	1.277
+++ src/window.c	30 May 2003 15:32:04 -0000
@@ -1438,6 +1438,20 @@ idle_calc_showing (gpointer data)
       
       tmp = tmp->next;
     }
+
+  /* for all displays used in the queue, set a sentinel property on
+   * the root window so that we can ignore EnterNotify events that
+   * occur before the window maps occur.  This avoids a race
+   * condition. */
+  tmp = should_show;
+  while (tmp != NULL)
+    {
+      MetaWindow *window = tmp->data;
+      meta_display_increment_focus_sentinel (window->display);
+
+      tmp = tmp->next;
+    }
+   
   
   g_slist_free (copy);
 
Index: src/workspace.c
===================================================================
RCS file: /cvs/gnome/metacity/src/workspace.c,v
retrieving revision 1.42
diff -p -u -r1.42 workspace.c
--- src/workspace.c	23 Feb 2003 17:09:46 -0000	1.42
+++ src/workspace.c	30 May 2003 15:32:04 -0000
@@ -196,7 +196,8 @@ meta_workspace_queue_calc_showing  (Meta
 }
 
 void
-meta_workspace_activate (MetaWorkspace *workspace)
+meta_workspace_activate_with_focus (MetaWorkspace *workspace,
+                                    MetaWindow    *focus_this)
 {
   MetaWorkspace *old;
   
@@ -218,8 +219,24 @@ meta_workspace_activate (MetaWorkspace *
   meta_workspace_queue_calc_showing (old);
   meta_workspace_queue_calc_showing (workspace);
 
-  meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n");
-  meta_screen_focus_default_window (workspace->screen, NULL);
+  if (focus_this)
+    {
+      meta_window_focus (focus_this,
+                         meta_display_get_current_time (focus_this->display));
+      meta_window_raise (focus_this);
+    }
+  else
+    {
+      meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n");
+      meta_screen_focus_default_window (workspace->screen, NULL);
+    }
+}
+
+void
+meta_workspace_activate (MetaWorkspace *workspace)
+{
+  meta_workspace_activate_with_focus (workspace,
+                                      NULL);
 }
 
 int
Index: src/workspace.h
===================================================================
RCS file: /cvs/gnome/metacity/src/workspace.h,v
retrieving revision 1.14
diff -p -u -r1.14 workspace.h
--- src/workspace.h	23 Feb 2003 17:09:46 -0000	1.14
+++ src/workspace.h	30 May 2003 15:32:04 -0000
@@ -58,7 +58,9 @@ void           meta_workspace_relocate_w
 /* don't confuse with meta_window_visible_on_workspace() */
 gboolean       meta_workspace_contains_window (MetaWorkspace *workspace,
                                                MetaWindow  *window);
-void           meta_workspace_activate      (MetaWorkspace *workspace);
+void           meta_workspace_activate_with_focus (MetaWorkspace *workspace,
+                                                   MetaWindow    *focus_this);
+void           meta_workspace_activate            (MetaWorkspace *workspace);
 int            meta_workspace_index         (MetaWorkspace *workspace);
 GList*         meta_workspace_list_windows  (MetaWorkspace *workspace);
 
