Subject: Re: bug#13225: 24.3.50; Non-selected window has not mode-line-inactive face

From: Eli Zaretskii <[email-masked]>
Date: Wed, 19 Dec 2012 18:07:18 +0200

> Date: Wed, 19 Dec 2012 09:12:15 +0100
> From: martin rudalics <[email-masked]>
> 
> With emacs -Q do C-x 5 2.  The mode lines of both windows appear in
> `mode-line' face, regardless of which window is selected.
> 
> This contradicts the Elisp manual which says:
> 
>        The selected window's mode line is usually displayed in a different
>     color using the face `mode-line'.  Other windows' mode lines appear in
>     the face `mode-line-inactive' instead.

We are shooting ourselves in the foot.  At some point during
redisplay, it loops over all the frames and redisplays every window of
every visible frame.  Here's the beginning of that loop:

      FOR_EACH_FRAME (tail, frame)
	{
	  struct frame *f = XFRAME (frame);

	  /* We don't have to do anything for unselected terminal
	     frames.  */
	  if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f))
	      && !EQ (FRAME_TTY (f)->top_frame, frame))
	    continue;

	  if (FRAME_WINDOW_P (f) || FRAME_TERMCAP_P (f) || f == sf)
	    {
	      /* Select the frame, for the sake of frame-local variables.  */
	      ensure_selected_frame (frame);

	      /* Mark all the scroll bars to be removed; we'll redeem
		 the ones we want when we redisplay their windows.  */
	      if (FRAME_TERMINAL (f)->condemn_scroll_bars_hook)
		FRAME_TERMINAL (f)->condemn_scroll_bars_hook (f);

	      if (FRAME_VISIBLE_P (f) && !FRAME_OBSCURED_P (f))
		redisplay_windows (FRAME_ROOT_WINDOW (f));

Where we now call ensure_selected_frame, originally there was a call
to select_frame_for_redisplay, which arranged for all the frame-local
variables to appear in C variables.  But now it does this in addition:

  selected_frame = frame;
  /* If redisplay causes scrolling, it sets point in the window, so we need to
     be careful with the selected-window's point handling.  */
  select_window_1 (XFRAME (frame)->selected_window, 0);

This selects the frame, and _also_ makes that frame's selected window
be the global selected_window.  Therefore, when display_mode_lines
comes to select a proper face for the mode line, it always finds the
frame's selected window in selected_window, and thus always uses the
face for selected windows.

I can fix this with the kludge shown below, but do we care about yet
another global variable, in addition to selected_window?  If we don't
want this, then the only other way I see is to drag this window all
the way down to display_mode_lines through the calling sequences.
(That's assuming that only the mode-line display wants to know about
the _real_ selected_window.)

=== modified file 'src/xdisp.c'
--- src/xdisp.c	2012-12-17 19:17:06 +0000
+++ src/xdisp.c	2012-12-19 16:02:53 +0000
@@ -13006,6 +13006,8 @@ do { if (polling_stopped_here) start_pol
 /* Perhaps in the future avoid recentering windows if it
    is not necessary; currently that causes some problems.  */
 
+static struct window *sw_on_sf;
+
 static void
 redisplay_internal (void)
 {
@@ -13491,6 +13493,8 @@ redisplay_internal (void)
 
 	  if (FRAME_WINDOW_P (f) || FRAME_TERMCAP_P (f) || f == sf)
 	    {
+	      sw_on_sf = sw;
+
 	      /* Select the frame, for the sake of frame-local variables.  */
 	      ensure_selected_frame (frame);
 
@@ -20371,7 +20375,7 @@ display_mode_lines (struct window *w)
 
   if (WINDOW_WANTS_MODELINE_P (w))
     {
-      struct window *sel_w = XWINDOW (old_selected_window);
+      struct window *sel_w = sw_on_sf;
 
       /* Select mode line face based on the real selected window.  */
       display_mode_line (w, CURRENT_MODE_LINE_FACE_ID_3 (sel_w, sel_w, w),