To make choice of focus window consistent for each focus method, a
number of guidelines should be followed.  (For purposes of discussion
here, I'm excluding things like the panel and the desktop from
"windows".  It is technically incorrect to do this, but I'm lazy and
"windows" is shorter than something like "normal windows".  See the
end of the discussion for how these special cases are handled.)  The
basics are easy:

Focus method  Behavior
    click     When a user clicks on a window, focus it
   sloppy     When an EnterNotify is received, focus the window
    mouse     Same as sloppy, but also defocus when mouse enters DESKTOP
              window

Note that these choices (along with the choice that clicking on a
window raises it for the click focus method) introduces the following
invariants for focus from mouse activity:

Focus method  Invariant
    click     The window on top is focused
   sloppy     If the mouse is in a window, then it is focused; if the
              mouse is not in a window, then the most recently used
              window is focused.
    mouse     If the mouse is in a non-DESKTOP window, then it is focused;
              otherwise, the designated "no_focus_window" is focused

However, there are a number of cases where the current focus window
becomes invalid and another should be chosen.  Some examples are when
a focused window is closed or minimized, or when the user changes
workspaces.  In these cases, there needs to be a rule consistent with
the above about the new window to choose.

Focus method  Behavior
    click     Focus the most recently used window (same as the window
              on top)
   sloppy     Focus the window containing the pointer if there is such
              a window, otherwise focus the most recently used window.
    mouse     Focus the non-DESKTOP window containing the pointer if
              there is one, otherwise focus the designated
              "no_focus_window".

Note that "most recently used window", as used here, has a slightly
different connotation than "most recent to have keyboard focus".  This
is because when a user activates a window that is a transient, its
ancestor(s) should be considered to be more recently used than other
windows that have had the keyboard focus more recently.  (See bug
157360; this may mean that the alt-tab order should also change
simultaneously, although the current implementation does not do that.)

Also, sometimes a new window will be mapped (e.g. unminimizing a
window or launching a new application).  Most users want to interact
with new windows right away, so these should typically be focused.
This does conflict with the invariants for sloppy and mouse focus
modes, so this wouldn't be true for a strict-pointer-focus mode.  For
all other modes (non-strict-pointer-focus modes), there are only two
cases in which a new window shouldn't be focused:

  1) If the window takes a while to launch and the user starts
     interacting with a different application, the new window should
     not take focus.
  2) If the window that will appear was not launched by the user
     (error dialogs, instant messaging windows, etc.), then the window
     should not take focus when it appears.

To handle these cases, Metacity compares timestamps of the event that
caused the launch and the timestamp of the last interaction with the
focused window.  (Case 2 is handled by the application providing a
special timestamp of 0 for the launch time, which ensures that the
window that appears doesn't get focus)

If the newly launched window isn't focused, some things should be done
to alert the user that there is a window to work with:
  1) The _NET_WM_DEMANDS_ATTENTION hint should be set
  2) If the new window isn't modal for the focused window, it should
     appear below the focused window so that it doesn't obscure the
     focused window that the user is interacting with.
  3) If the new window is modal to the focused window, the currently
     focused window should lose focus but the modal window should
     appear on top.

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 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 events from mouse movement, set
     mouse_mode to TRUE.
  2) When using keynav to choose a focus window (e.g. alt-tab, alt-esc,
     alt-f2, 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. switch-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
of decorations.  For WM_DESKTOP windows, we only focus them if the
user explicitly requests it (e.g. clicks on the window, uses
Ctrl-Alt-Tab to navigate to it, uses a keybinding to show the desktop,
etc.).  For WM_DOCK windows, we do not focus unless we receive a very
explicit request (e.g. Ctrl-Alt-Tab or a _NET_ACTIVE_WINDOW message;
not normal clicks).




To read more about the bugs that inspired these choices:
  - When a focused window becomes invalid and another should be chosen
    http://bugzilla.gnome.org/show_bug.cgi?id=135810
  - When a new window is mapped
    http://bugzilla.gnome.org/show_bug.cgi?id=118372
    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
  - 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
    http://bugzilla.gnome.org/show_bug.cgi?id=357695
  - Not focusing panels
    http://bugzilla.gnome.org/show_bug.cgi?id=160470
    http://bugzilla.gnome.org/show_bug.cgi?id=120100

There were many bugs which had to be fixed to get all the above
working; they helped form these policies and/or show the difficulties
in implementing this policy (my apologies in advance for producing a
list heavily lopsided to what I've done; it's just that these bugs are
the ones I'm the most familiar with):
  bug  72314  ignore LeaveNotify events from grabs
  bug  82921  focus windows on map
  bug  87531  only show focus for sticky windows on active workspace (pager)
  bug  94545  focus window on workspace switch is non-deterministic
  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
  bug 115650  add _NET_WM_USER_TIME support to gtk+ (see also 150502)
  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
  bug 135024  _NET_ACTIVE_WINDOW messages need timestamps
  bug 135786  middle-clicking on focused window to lower it should defocus too
  bug 136581  window minimization vs. activation for mouse focus
  bug 144900  fix focus choice on "un-showing" the desktop
  bug 147475  don't lock keyboard on workspace change
  bug 148364  DEMANDS_ATTENTION support for metacity & libwnck (and other stuff)
  bug 149028  focus-stealing-prevention for metacity-dialog (and other stuff)
  bug 149366  windows denied focus on map occur in wrong order in alt-tab list
  bug 149543  consistent focus window when unshowing desktop
  bug 149589  race in focus choice from libwnck messages
  bug 150271  make sure "run application" dialog gets focused
  bug 150668  update gtk+ _NET_ACTIVE_WINDOW support
  bug 151245  application startup notification forwarding (partially rejected)
  bug 151984  Soeren's idea--backup timestamp when startup notification not used
  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
  You may occasionally hear people refer to sloppy or mouse focus
  modes as inherently buggy.  This is what they mean by that:

  1) Keynav doesn't maintain the same invariants as mouse navigation
     for these focus modes; switching back and forth between
     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
        mouse is does not have focus.
     b) Users expect that a workspace they previously used will not
        change when the return to it.  This means things like window
        position and stacking order, but also the focus window.
        Unfortunately, using the original focus window (which would be
        the most recently used window on that workspace) will
        sometimes conflict with the invariants for mouse and sloppy
        focus modes.  Users are much more surprised by the invariant
        being broken than by having the focus window changed (see bug
        94545 and probably others), so we maintain the invariant.
        This only matters when using Ctrl-Alt-Arrow to switch
        workspaces instead of clicking in the workspace switcher, so
        this really is a keynav vs mouse issue.  Either that, or a
        windows-are-being-mapped exception.  ;-)
     c) Opening a menu, then moving the mouse to a different window,
        and then pressing escape to dismiss the menu will result in
        the window containing the mouse not being focused.  This is
        actually correct behavior (because pressing escape shows that
        the user is using key navigation to interact with the window
        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
     confusing)
  3) There are an awful lot of little cases to handle to get any focus
     mode right, even for click-to-focus.  Since mouse and sloppy
     focus have sometimes been hard to even determine what correct
     behavior is, it is much harder to get them completely right.
     Plus mouse and sloppy focus users are a minority, decreasing the
     motivation of window manager implementors to get those focus
     modes right.
  4) Because of -1-, -2-, and -3-, implementations are often buggy or
     inconsistent and people form their opinions from usage of these
     implementations.
  5) Sloppy focus suffers from a bit of a discoverability problem (for
     example, I have seen a scientist sit down to a computer for which
     sloppy focus was in use and take a few minutes before figuring
     out how window activation worked; granted the layout of the
     windows in that situation was a bit unusual but it still
     illustrates that sloppy focus is harder than it should be to
     figure out).  Mouse focus solves this problem; however, people
     that have experience with other computing environments are
     accustomed to being able to move their mouse outside the window
     they are working with and still continue interacting with that
     window, which conflicts with mouse focus.
