Index: doc/how-to-get-focus-right.txt
===================================================================
RCS file: /cvs/gnome/metacity/doc/how-to-get-focus-right.txt,v
retrieving revision 1.5
diff -p -u -r1.5 how-to-get-focus-right.txt
--- doc/how-to-get-focus-right.txt	2 Feb 2005 18:46:09 -0000	1.5
+++ doc/how-to-get-focus-right.txt	21 Feb 2005 23:00:41 -0000
@@ -79,9 +79,26 @@ to alert the user that there is a window
 Additionally, the user may decide to use the keyboard instead of the mouse
 to navigate between windows (referred to as "keynav").  This poses no
 problems for click-to-focus (because the same invariant can be
-maintained), but for sloppy and mouse focus it means that EnterNotify
-and LeaveNotify events should be ignored (they can be generated
-without using the mouse, for example, by grabs).
+maintained), but for sloppy and mouse focus it requires extra work to
+attempt to handle the INHERENTLY CONFLICTING CONSTRAINTS.  Metacity does
+this by having a mouse_mode boolean used to determine which of the two
+sets of invariants holds.  This mode is set according to which method was
+most recently used to choose a focus window:
+  1) When receiving EnterNotify/LeaveNotify events from mouse movement, set
+     mouse_mode to TRUE.
+  2) When using keynav to choose a focus window (e.g. alt-tab, alt-esc,
+     move-window-to-workspace keybindings), set mouse_mode to FALSE.
+  3) When handling events that don't choose a focus window but rather need
+     a focus_window chosen for them (e.g. move-to-workspace keybindings),
+     don't change the mouse_mode and just use the current value.
+Note that grabs present a special case since they can generate EnterNotify
+and LeaveNotify events without using the mouse, thus these events should be
+ignored when the crossing mode is NotifyGrab or NotifyUngrab.  THIS
+MOUSENAV/KEYNAV MODERATION METHOD IS NOT PERFECT--there are corner cases
+when trying to mix-and-match between mousenav and keynav simultaneously
+that cause problems; but it appears to be the most reasonable tradeoff and
+works well in most cases, especially if the user sticks to just mousenav
+for a long time or just keynav for a long time.
 
 Finally, windows of type WM_DOCK or WM_DESKTOP (e.g. the desktop and
 the panel) present a special case, at least partially due to the lack
@@ -103,7 +120,8 @@ To read more about the bugs that inspire
     Also, the EWMH spec, especially the parts relating to _NET_WM_USER_TIME
   - Modal vs. non-modal dialogs that get denied focus when mapped
     http://bugzilla.gnome.org/show_bug.cgi?id=151996
-  - Ignoring EnterNotify and LeaveNotify events during keynav
+  - Mousenav vs. Keynav in mouse and sloppy focus modes
+    http://bugzilla.gnome.org/show_bug.cgi?id=167545
     http://bugzilla.gnome.org/show_bug.cgi?id=101190
   - Not focusing panels
     http://bugzilla.gnome.org/show_bug.cgi?id=160470
@@ -121,6 +139,7 @@ the ones I'm the most familiar with):
   bug  95747  should ignore EnterNotify events with NotifyInferior detail set
   bug  97635  sticky windows always keep focus when switching workspaces
   bug 102665  a window unminimized from the tasklist should be focused
+  bug 107347  focus windows that manually position themselves too
   bug 108643  focus in MRU order instead of stack order
   bug 110970  moving a window to another workspace loses focus
   bug 112031  closing a dialog can result in a strange focus window
@@ -128,6 +147,7 @@ the ones I'm the most familiar with):
   bug 120100  panel shouldn't be focused after workspace applet usage
   bug 123803  need final EnterNotify after workspace switch (see also 124798)
   bug 124981  focus clicked window in pager only if on current workspace
+  bug 125492  catch the xserver unfocusing everything and fix its braindeadedness
   bug 128200  focus correct window on libwnck window minimize (see 107681 too)
   bug 131582  fix race condition on window minimize/close
   bug 133120  wrong window focused when changing workspaces
@@ -148,6 +168,18 @@ the ones I'm the most familiar with):
   bug 151990  prevent focus inconsistencies by only providing one focus method
   bug 151996  modal dialogs denied focus should not be lowered
   bug 152000  fix race on window close followed by rapid mouse movement
+  bug 152004  ways to handle new window versus mouse invariants
+  bug 153220  catch the root window getting focus and reset to default window
+  bug 157360  focus parents of dismissed transient windows in preference to
+              the window that most recently had focus
+  bug 159257  focus the desktop when showing it
+  bug 160470  don't focus panels on click
+  bug 163450  correct highlighting in workspace switcher popup
+  bug 164716  refuse to focus a window with a modal transient, and focus
+              the transient instead
+  bug 166524  avoid new windows being obscured by the focus window
+  bug 167545  mousenav vs. keynav in mouse and sloppy focus modes
+  <a massive heap of bugs relating to focus stealing prevention...>
 
 
 Addendum on sloppy and mouse focus
@@ -156,7 +188,7 @@ Addendum on sloppy and mouse focus
 
   1) Keynav doesn't maintain the same invariants as mouse navigation
      for these focus modes; switching back and forth between
-     navigation methods, therefore, may appear to have
+     navigation methods, therefore, may have or appear to have
      inconsistencies.  Examples:
      a) If the user uses Alt-Tab to change the window with focus, then
         starts to move the mouse, at that moment the window where the
@@ -182,6 +214,15 @@ Addendum on sloppy and mouse focus
         containing the menu) but is one of those hard-to-get-right
         keynav and mouse focus mixture cases.  (See bug 101190 for
         more details)
+     d) Similar to (c), moving the mouse off the menu doesn't immediately
+        focus the window that the mouse goes over, due to an application
+        grab (we couldn't change this and wouldn't want to, but
+        technically it does break the invariant).
+     e) If mouse_mode is off and the user does something to cause focus to
+        change (e.g. switch workspaces, close or minimize a window, etc.)
+        and simultaneously tries to move the mouse, the choice of which
+        window to focus is inherently race-y.  (You probably can't satisfy
+        both keynav and mousenav invariants simultaneously...)
   2) The sloppy/mouse invariants are often not strictly maintained;
      for example, we provide an exception to the invariant for newly
      mapped windows.  (Most find that not allowing this exception is
Index: src/display.c
===================================================================
RCS file: /cvs/gnome/metacity/src/display.c,v
retrieving revision 1.254
diff -p -u -r1.254 display.c
--- src/display.c	20 Feb 2005 23:38:31 -0000	1.254
+++ src/display.c	21 Feb 2005 23:00:41 -0000
@@ -343,6 +343,8 @@ meta_display_open (const char *name)
   display->expected_focus_window = NULL;
   display->grab_old_window_stacking = NULL;
 
+  display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
+
 #ifdef HAVE_XSYNC
   display->grab_sync_request_alarm = None;
 #endif
@@ -1785,10 +1787,15 @@ event_callback (XEvent   *event,
                   window->type != META_WINDOW_DESKTOP)
                 {
                   meta_topic (META_DEBUG_FOCUS,
-                              "Focusing %s due to enter notify with serial %lu\n",
-                              window->desc, event->xany.serial);
+                              "Focusing %s due to enter notify with serial %lu "
+                              "at time %lu, and setting display->mouse_mode to "
+                              "TRUE.\n",
+                              window->desc, 
+                              event->xany.serial,
+                              event->xcrossing.time);
 
-		  meta_window_focus (window, event->xcrossing.time);
+                  display->mouse_mode = TRUE;
+                  meta_window_focus (window, event->xcrossing.time);
 
 		  /* stop ignoring stuff */
 		  reset_ignores (display);
@@ -1822,16 +1829,28 @@ event_callback (XEvent   *event,
           switch (meta_prefs_get_focus_mode ())
             {
             case META_FOCUS_MODE_MOUSE:
-              if (window == display->expected_focus_window &&
-                  (window->frame == NULL || frame_was_receiver) &&
+              if ((window->frame == NULL || frame_was_receiver) &&
 		  event->xcrossing.mode != NotifyGrab && 
 		  event->xcrossing.mode != NotifyUngrab &&
 		  event->xcrossing.detail != NotifyInferior)
                 {
-                  meta_verbose ("Unsetting focus from %s due to LeaveNotify\n",
-                                window->desc);
-                  meta_display_focus_the_no_focus_window (display, 
-                                                          event->xcrossing.time);
+                  if (window == display->expected_focus_window)
+                    {
+                      meta_topic (META_DEBUG_FOCUS,
+                                  "Unsetting focus from %s due to LeaveNotify\n",
+                                  window->desc);
+                      meta_display_focus_the_no_focus_window (display, 
+                                                              event->xcrossing.time);
+                    }
+                  if (window->type != META_WINDOW_DOCK &&
+                      window->type != META_WINDOW_DESKTOP)
+                    {
+                      meta_topic (META_DEBUG_FOCUS,
+                                  "Setting display->mouse_mode to TRUE due to "
+                                  "LeaveNotify at time %lu.\n",
+                                  event->xcrossing.time);
+                      display->mouse_mode = TRUE;
+                    }
                 }
               break;
             case META_FOCUS_MODE_SLOPPY:
Index: src/display.h
===================================================================
RCS file: /cvs/gnome/metacity/src/display.h,v
retrieving revision 1.130
diff -p -u -r1.130 display.h
--- src/display.h	20 Feb 2005 17:14:16 -0000	1.130
+++ src/display.h	21 Feb 2005 23:00:41 -0000
@@ -204,6 +204,11 @@ struct _MetaDisplay
   /* last user interaction time in any app */
   Time last_user_time;
 
+  /* whether we're using mousenav (only relevant for sloppy&mouse focus modes;
+   * !mouse_mode means "keynav mode")
+   */
+  guint mouse_mode : 1;
+
   guint static_gravity_works : 1;
   
   /*< private-ish >*/
Index: src/keybindings.c
===================================================================
RCS file: /cvs/gnome/metacity/src/keybindings.c,v
retrieving revision 1.109
diff -p -u -r1.109 keybindings.c
--- src/keybindings.c	24 Jan 2005 05:58:30 -0000	1.109
+++ src/keybindings.c	21 Feb 2005 23:00:43 -0000
@@ -2378,8 +2378,10 @@ process_tab_grab (MetaDisplay *display,
           meta_topic (META_DEBUG_KEYBINDINGS,
                       "Activating target window\n");
 
-          meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup selection\n",
+          meta_topic (META_DEBUG_FOCUS, "Activating %s due to tab popup "
+                      "selection and turning mouse_mode off\n",
                       target_window->desc);
+          display->mouse_mode = FALSE;
           meta_window_activate (target_window, event->xkey.time);
 
           meta_topic (META_DEBUG_KEYBINDINGS,
@@ -2993,8 +2995,11 @@ do_choose_window (MetaDisplay    *displa
           /* If no modifiers, we can't do the "hold down modifier to keep
            * moving" thing, so we just instaswitch by one window.
            */
-          meta_topic (META_DEBUG_FOCUS, "Activating %s due to switch/cycle windows with no modifiers\n",
+          meta_topic (META_DEBUG_FOCUS,
+                      "Activating %s and turning off mouse_mode due to "
+                      "switch/cycle windows with no modifiers\n",
                       initial_selection->desc);
+          display->mouse_mode = FALSE;
           meta_window_activate (initial_selection, event->xkey.time);
         }
       else if (meta_display_begin_grab_op (display,
@@ -3017,9 +3022,13 @@ do_choose_window (MetaDisplay    *displa
                * before we establish the grab. must end grab
                * prior to trying to focus a window.
                */
-              meta_topic (META_DEBUG_FOCUS, "Ending grab and activating %s due to switch/cycle windows where modifier was released prior to grab\n",
+              meta_topic (META_DEBUG_FOCUS, 
+                          "Ending grab, activating %s, and turning off "
+                          "mouse_mode due to switch/cycle windows where "
+                          "modifier was released prior to grab\n",
                           initial_selection->desc);
               meta_display_end_grab_op (display, event->xkey.time);
+              display->mouse_mode = FALSE;
               meta_window_activate (initial_selection, event->xkey.time);
             }
           else
@@ -3275,7 +3284,15 @@ 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_with_focus (workspace, window, event->xkey.time);
+        {
+          meta_topic (META_DEBUG_FOCUS,
+                      "Resetting mouse_mode to FALSE due to "
+                      "do_handle_move_to_workspace() call with flip set.\n");
+          workspace->screen->display->mouse_mode = FALSE;
+          meta_workspace_activate_with_focus (workspace,
+                                              window,
+                                              event->xkey.time);
+        }
     }
   else
     {
Index: src/window.c
===================================================================
RCS file: /cvs/gnome/metacity/src/window.c,v
retrieving revision 1.380
diff -p -u -r1.380 window.c
--- src/window.c	20 Feb 2005 22:44:15 -0000	1.380
+++ src/window.c	21 Feb 2005 23:00:43 -0000
@@ -1471,6 +1471,25 @@ idle_calc_showing (gpointer data)
       tmp = tmp->next;
     }
 
+  if (meta_prefs_get_focus_mode () != META_FOCUS_MODE_CLICK)
+    {
+      /* When display->mouse_mode is false, we want to ignore
+       * EnterNotify events unless they come from mouse motion.  To do
+       * that, we set a sentinel property on the root window if we're
+       * not in mouse_mode.
+       */
+      tmp = should_show;
+      while (tmp != NULL)
+        {
+          MetaWindow *window = tmp->data;
+          
+          if (!window->display->mouse_mode)
+            meta_display_increment_focus_sentinel (window->display);
+
+          tmp = tmp->next;
+        }
+    }
+
   g_slist_free (copy);
 
   g_slist_free (unplaced);
Index: src/workspace.c
===================================================================
RCS file: /cvs/gnome/metacity/src/workspace.c,v
retrieving revision 1.69
diff -p -u -r1.69 workspace.c
--- src/workspace.c	2 Feb 2005 18:46:09 -0000	1.69
+++ src/workspace.c	21 Feb 2005 23:00:44 -0000
@@ -317,6 +317,7 @@ meta_workspace_activate_with_focus (Meta
   meta_workspace_queue_calc_showing (old);
   meta_workspace_queue_calc_showing (workspace);
 
+  /* FIXME: Why do we need this?!?  Isn't it handled in the lines above? */
   if (move_window)
       /* Removes window from other spaces */
       meta_window_change_workspace (move_window, workspace);
@@ -800,7 +801,8 @@ meta_workspace_focus_default_window (Met
     }
 
 
-  if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK)
+  if (meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK ||
+      !workspace->screen->display->mouse_mode)
     focus_ancestor_or_mru_window (workspace, not_this_one, timestamp);
   else
     {
