]> code.delx.au - gnu-emacs/blobdiff - src/window.c
Set maintainer to FSF, since author cannot
[gnu-emacs] / src / window.c
index f156c4697038d7cd4d60c4d75b1d66725b4332dc..541da41aec818731989457d61c635307f44c7ffb 100644 (file)
@@ -62,12 +62,20 @@ static void window_scroll_line_based P_ ((Lisp_Object, int, int, int));
 static int window_min_size_1 P_ ((struct window *, int));
 static int window_min_size P_ ((struct window *, int, int, int *));
 static void size_window P_ ((Lisp_Object, int, int, int));
-static void foreach_window_1 P_ ((struct window *, void (*fn) (), int, int,
-                                 int, int));
-static void freeze_window_start P_ ((struct window *, int));
+static int freeze_window_start P_ ((struct window *, void *));
 static int window_fixed_size_p P_ ((struct window *, int, int));
 static void enlarge_window P_ ((Lisp_Object, int, int));
-
+static Lisp_Object window_list P_ ((void));
+static int add_window_to_list P_ ((struct window *, void *));
+static int candidate_window_p P_ ((Lisp_Object, Lisp_Object, Lisp_Object,
+                                  Lisp_Object));
+static Lisp_Object next_window P_ ((Lisp_Object, Lisp_Object,
+                                   Lisp_Object, int));
+static void decode_next_window_args P_ ((Lisp_Object *, Lisp_Object *,
+                                        Lisp_Object *));
+static int foreach_window_1 P_ ((struct window *,
+                                int (* fn) (struct window *, void *),
+                                void *));
 
 /* This is the window in which the terminal's cursor should
    be left when nothing is being done with it.  This must
@@ -79,6 +87,12 @@ static void enlarge_window P_ ((Lisp_Object, int, int));
 
 Lisp_Object selected_window;
 
+/* A list of all windows for use by next_window and Fwindow_list.
+   Functions creating or deleting windows should invalidate this cache
+   by setting it to nil.  */
+
+Lisp_Object Vwindow_list;
+
 /* The mini-buffer window of the selected frame.
    Note that you cannot test for mini-bufferness of an arbitrary window
    by comparing against this; but you can test for mini-bufferness of
@@ -238,6 +252,8 @@ make_window ()
   XSETWINDOW (val, p);
   XSETFASTINT (p->last_point, 0);
   p->frozen_window_start_p = 0;
+
+  Vwindow_list = Qnil;
   return val;
 }
 
@@ -458,10 +474,12 @@ coordinates_in_window (w, x, y)
   int left_x, right_x, top_y, bottom_y;
   int flags_area_width = FRAME_LEFT_FLAGS_AREA_WIDTH (f);
 
+  /* In what's below, we subtract 1 when computing right_x because we
+     want the rightmost pixel, which is given by left_pixel+width-1.  */
   if (w->pseudo_window_p)
     {
       left_x = 0;
-      right_x = XFASTINT (w->width) * CANON_Y_UNIT (f);
+      right_x = XFASTINT (w->width) * CANON_Y_UNIT (f) - 1;
       top_y = WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w);
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
     }
@@ -469,7 +487,7 @@ coordinates_in_window (w, x, y)
     {
       left_x = (WINDOW_DISPLAY_LEFT_EDGE_PIXEL_X (w)
                - FRAME_INTERNAL_BORDER_WIDTH_SAFE (f));
-      right_x = WINDOW_DISPLAY_RIGHT_EDGE_PIXEL_X (w);
+      right_x = WINDOW_DISPLAY_RIGHT_EDGE_PIXEL_X (w) - 1;
       top_y = (WINDOW_DISPLAY_TOP_EDGE_PIXEL_Y (w)
               - FRAME_INTERNAL_BORDER_WIDTH_SAFE (f));
       bottom_y = WINDOW_DISPLAY_BOTTOM_EDGE_PIXEL_Y (w);
@@ -584,50 +602,84 @@ If they are on the border between WINDOW and its right sibling,\n\
     }
 }
 
+
+/* Callback for foreach_window, used in window_from_coordinates.
+   Check if window W contains coordinates specified by USER_DATA which
+   is actually a pointer to a struct check_window_data CW.
+
+   Check if window W contains coordinates *CW->x and *CW->y.  If it
+   does, return W in *CW->window, as Lisp_Object, and return in
+   *CW->part the part of the window under coordinates *X/*Y.  Return
+   zero from this function to stop iterating over windows.  */
+
+struct check_window_data
+{
+  Lisp_Object *window;
+  int *x, *y, *part;
+};
+
+static int
+check_window_containing (w, user_data)
+     struct window *w;
+     void *user_data;
+{
+  struct check_window_data *cw = (struct check_window_data *) user_data;
+  int found;
+
+  found = coordinates_in_window (w, cw->x, cw->y);
+  if (found)
+    {
+      *cw->part = found - 1;
+      XSETWINDOW (*cw->window, w);
+    }
+  
+  return !found;
+}
+
+
 /* Find the window containing frame-relative pixel position X/Y and
    return it as a Lisp_Object.  If X, Y is on the window's modeline,
    set *PART to 1; if it is on the separating line between the window
    and its right sibling, set it to 2; otherwise set it to 0.  If
    there is no window under X, Y return nil and leave *PART
-   unmodified.  TOOL_BAR_P non-zero means detect tool-bar windows.  */
+   unmodified.  TOOL_BAR_P non-zero means detect tool-bar windows.
+
+   This function was previously implemented with a loop cycling over
+   windows with Fnext_window, and starting with the frame's selected
+   window.  It turned out that this doesn't work with an
+   implementation of next_window using Vwindow_list, because
+   FRAME_SELECTED_WINDOW (F) is not always contained in the window
+   tree of F when this function is called asynchronously from
+   note_mouse_highlight.  The original loop didn't terminate in this
+   case.  */
 
 Lisp_Object
-window_from_coordinates (frame, x, y, part, tool_bar_p)
-     FRAME_PTR frame;
+window_from_coordinates (f, x, y, part, tool_bar_p)
+     struct frame *f;
      int x, y;
      int *part;
      int tool_bar_p;
 {
-  register Lisp_Object tem, first;
-  int found;
-
-  tem = first = FRAME_SELECTED_WINDOW (frame);
-
-  do
-    {
-      found = coordinates_in_window (XWINDOW (tem), &x, &y);
-
-      if (found)
-       {
-         *part = found - 1;
-         return tem;
-       }
-
-      tem = Fnext_window (tem, Qt, Qlambda);
-    }
-  while (!EQ (tem, first));
+  Lisp_Object window;
+  struct check_window_data cw;
 
-  /* See if it's in the tool bar window, if a tool bar exists.  */
-  if (tool_bar_p
-      && WINDOWP (frame->tool_bar_window)
-      && XFASTINT (XWINDOW (frame->tool_bar_window)->height)
-      && coordinates_in_window (XWINDOW (frame->tool_bar_window), &x, &y))
+  window = Qnil;
+  cw.window = &window, cw.x = &x, cw.y = &y; cw.part = part;
+  foreach_window (f, check_window_containing, &cw);
+  
+  /* If not found above, see if it's in the tool bar window, if a tool
+     bar exists.  */
+  if (NILP (window)
+      && tool_bar_p
+      && WINDOWP (f->tool_bar_window)
+      && XINT (XWINDOW (f->tool_bar_window)->height) > 0
+      && coordinates_in_window (XWINDOW (f->tool_bar_window), &x, &y))
     {
       *part = 0;
-      return frame->tool_bar_window;
+      window = f->tool_bar_window;
     }
 
-  return Qnil;
+  return window;
 }
 
 DEFUN ("window-at", Fwindow_at, Swindow_at, 2, 3, 0,
@@ -1019,6 +1071,7 @@ delete_window (window)
   par = XWINDOW (parent);
 
   windows_or_buffers_changed++;
+  Vwindow_list = Qnil;
   frame = XFRAME (WINDOW_FRAME (p));
   FRAME_WINDOW_SIZES_CHANGED (frame) = 1;
 
@@ -1131,9 +1184,237 @@ delete_window (window)
   adjust_glyphs (frame);
   UNBLOCK_INPUT;
 }
+
+
 \f
+/***********************************************************************
+                            Window List
+ ***********************************************************************/
+
+/* Add window W to *USER_DATA.  USER_DATA is actually a Lisp_Object
+   pointer.  This is a callback function for foreach_window, used in
+   function window_list.  */
+
+static int
+add_window_to_list (w, user_data)
+     struct window *w;
+     void *user_data;
+{
+  Lisp_Object *list = (Lisp_Object *) user_data;
+  Lisp_Object window;
+  XSETWINDOW (window, w);
+  *list = Fcons (window, *list);
+  return 1;
+}
+
+
+/* Return a list of all windows, for use by next_window.  If
+   Vwindow_list is a list, return that list.  Otherwise, build a new
+   list, cache it in Vwindow_list, and return that.  */
+
+static Lisp_Object
+window_list ()
+{
+  if (!CONSP (Vwindow_list))
+    {
+      Lisp_Object tail;
+
+      Vwindow_list = Qnil;
+      for (tail = Vframe_list; CONSP (tail); tail = XCDR (tail))
+       {
+         Lisp_Object args[2];
+
+         /* We are visiting windows in canonical order, and add
+            new windows at the front of args[1], which means we
+            have to reverse this list at the end.  */
+         args[1] = Qnil;
+         foreach_window (XFRAME (XCAR (tail)), add_window_to_list, &args[1]);
+         args[0] = Vwindow_list;
+         args[1] = Fnreverse (args[1]);
+         Vwindow_list = Fnconc (2, args);
+       }
+    }
+  
+  return Vwindow_list;
+}
+
+
+/* Value is non-zero if WINDOW satisfies the constraints given by
+   OWINDOW, MINIBUF and ALL_FRAMES.
+
+   MINIBUF     t means WINDOW may be minibuffer windows.
+               `lambda' means WINDOW may not be a minibuffer window.
+               a window means a specific minibuffer window
+
+   ALL_FRAMES  t means search all frames,
+               nil means search just current frame,
+               `visible' means search just visible frames,
+               0 means search visible and iconified frames,
+               a window means search the frame that window belongs to,
+               a frame means consider windows on that frame, only.  */
+
+static int
+candidate_window_p (window, owindow, minibuf, all_frames)
+     Lisp_Object window, owindow, minibuf, all_frames;
+{
+  struct window *w = XWINDOW (window);
+  struct frame *f = XFRAME (w->frame);
+  int candidate_p = 1;
+
+  if (!BUFFERP (w->buffer))
+    candidate_p = 0;
+  else if (MINI_WINDOW_P (w)
+           && (EQ (minibuf, Qlambda)
+              || (WINDOWP (minibuf) && !EQ (minibuf, window))))
+    {
+      /* If MINIBUF is `lambda' don't consider any mini-windows.
+         If it is a window, consider only that one.  */
+      candidate_p = 0;
+    }
+  else if (EQ (all_frames, Qt))
+    candidate_p = 1;
+  else if (NILP (all_frames))
+    {
+      xassert (WINDOWP (owindow));
+      candidate_p = EQ (w->frame, XWINDOW (owindow)->frame);
+    }
+  else if (EQ (all_frames, Qvisible))
+    {
+      FRAME_SAMPLE_VISIBILITY (f);
+      candidate_p = FRAME_VISIBLE_P (f);
+    }
+  else if (INTEGERP (all_frames) && XINT (all_frames) == 0)
+    {
+      FRAME_SAMPLE_VISIBILITY (f);
+      candidate_p = FRAME_VISIBLE_P (f) || FRAME_ICONIFIED_P (f);
+    }
+  else if (WINDOWP (all_frames))
+    candidate_p = (EQ (FRAME_MINIBUF_WINDOW (f), all_frames)
+                  || EQ (XWINDOW (all_frames)->frame, w->frame)
+                  || EQ (XWINDOW (all_frames)->frame, FRAME_FOCUS_FRAME (f)));
+  else if (FRAMEP (all_frames))
+    candidate_p = EQ (all_frames, w->frame);
+
+  return candidate_p;
+}
+
+
+/* Decode arguments as allowed by Fnext_window, Fprevious_window, and
+   Fwindow_list.  See there for the meaning of WINDOW, MINIBUF, and
+   ALL_FRAMES.  */
+
+static void
+decode_next_window_args (window, minibuf, all_frames)
+     Lisp_Object *window, *minibuf, *all_frames;
+{
+  if (NILP (*window))
+    *window = selected_window;
+  else
+    CHECK_LIVE_WINDOW (*window, 0);
+  
+  /* MINIBUF nil may or may not include minibuffers.  Decide if it
+     does.  */
+  if (NILP (*minibuf))
+    *minibuf = minibuf_level ? minibuf_window : Qlambda;
+  else if (!EQ (*minibuf, Qt))
+    *minibuf = Qlambda;
+  
+  /* Now *MINIBUF can be t => count all minibuffer windows, `lambda'
+     => count none of them, or a specific minibuffer window (the
+     active one) to count.  */
+
+  /* ALL_FRAMES nil doesn't specify which frames to include.  */
+  if (NILP (*all_frames))
+    *all_frames = (!EQ (*minibuf, Qlambda)
+                  ? FRAME_MINIBUF_WINDOW (XFRAME (XWINDOW (*window)->frame))
+                  : Qnil);
+  else if (EQ (*all_frames, Qvisible))
+    ;
+  else if (XFASTINT (*all_frames) == 0)
+    ;
+  else if (FRAMEP (*all_frames))
+    ;
+  else if (!EQ (*all_frames, Qt))
+    *all_frames = Qnil;
+  
+  /* Now *ALL_FRAMES is t meaning search all frames, nil meaning
+     search just current frame, `visible' meaning search just visible
+     frames, 0 meaning search visible and iconified frames, or a
+     window, meaning search the frame that window belongs to, or a
+     frame, meaning consider windows on that frame, only.  */
+}
+
+
+/* Return the next or previous window of WINDOW in canonical ordering
+   of windows.  NEXT_P non-zero means return the next window.  See the
+   documentation string of next-window for the meaning of MINIBUF and
+   ALL_FRAMES.  */
+
+static Lisp_Object
+next_window (window, minibuf, all_frames, next_p)
+     Lisp_Object window, minibuf, all_frames;
+     int next_p;
+{
+  decode_next_window_args (&window, &minibuf, &all_frames);
+  
+  /* If ALL_FRAMES is a frame, and WINDOW isn't on that frame, just
+     return the first window on the frame.  */
+  if (FRAMEP (all_frames)
+      && !EQ (all_frames, XWINDOW (window)->frame))
+    return Fframe_first_window (all_frames);
+  
+  if (next_p)
+    {
+      Lisp_Object list;
+      
+      /* Find WINDOW in the list of all windows.  */
+      list = Fmemq (window, window_list ());
+
+      /* Scan forward from WINDOW to the end of the window list.  */
+      if (CONSP (list))
+       for (list = XCDR (list); CONSP (list); list = XCDR (list))
+         if (candidate_window_p (XCAR (list), window, minibuf, all_frames))
+           break;
+
+      /* Scan from the start of the window list up to WINDOW.  */
+      if (!CONSP (list))
+       for (list = Vwindow_list;
+            CONSP (list) && !EQ (XCAR (list), window);
+            list = XCDR (list))
+         if (candidate_window_p (XCAR (list), window, minibuf, all_frames))
+           break;
+
+      if (CONSP (list))
+       window = XCAR (list);
+    }
+  else
+    {
+      Lisp_Object candidate, list;
+      
+      /* Scan through the list of windows for candidates.  If there are
+        candidate windows in front of WINDOW, the last one of these
+        is the one we want.  If there are candidates following WINDOW
+        in the list, again the last one of these is the one we want.  */
+      candidate = Qnil;
+      for (list = window_list (); CONSP (list); list = XCDR (list))
+       {
+         if (EQ (XCAR (list), window))
+           {
+             if (WINDOWP (candidate))
+               break;
+           }
+         else if (candidate_window_p (XCAR (list), window, minibuf,
+                                      all_frames))
+           candidate = XCAR (list);
+       }
+
+      if (WINDOWP (candidate))
+       window = candidate;
+    }
+
+  return window;
+}
 
-extern Lisp_Object next_frame (), prev_frame ();
 
 /* This comment supplies the doc string for `next-window',
    for make-docfile to see.  We cannot put this in the real DEFUN
@@ -1170,114 +1451,12 @@ windows, eventually ending up back at the window you started with.\n\
 DEFUN ("next-window", Fnext_window, Snext_window, 0, 3, 0,
        0)
   (window, minibuf, all_frames)
-     register Lisp_Object window, minibuf, all_frames;
+     Lisp_Object window, minibuf, all_frames;
 {
-  register Lisp_Object tem;
-  Lisp_Object start_window;
-
-  if (NILP (window))
-    window = selected_window;
-  else
-    CHECK_LIVE_WINDOW (window, 0);
-
-  start_window = window;
-
-  /* minibuf == nil may or may not include minibuffers.
-     Decide if it does.  */
-  if (NILP (minibuf))
-    minibuf = (minibuf_level ? minibuf_window : Qlambda);
-  else if (! EQ (minibuf, Qt))
-    minibuf = Qlambda;
-  /* Now minibuf can be t => count all minibuffer windows,
-     lambda => count none of them,
-     or a specific minibuffer window (the active one) to count.  */
-
-  /* all_frames == nil doesn't specify which frames to include.  */
-  if (NILP (all_frames))
-    all_frames = (! EQ (minibuf, Qlambda)
-                 ? (FRAME_MINIBUF_WINDOW
-                    (XFRAME
-                     (WINDOW_FRAME
-                      (XWINDOW (window)))))
-                 : Qnil);
-  else if (EQ (all_frames, Qvisible))
-    ;
-  else if (XFASTINT (all_frames) == 0)
-    ;
-  else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window)))
-    /* If all_frames is a frame and window arg isn't on that frame, just
-       return the first window on the frame.  */
-    return Fframe_first_window (all_frames);
-  else if (! EQ (all_frames, Qt))
-    all_frames = Qnil;
-  /* Now all_frames is t meaning search all frames,
-     nil meaning search just current frame,
-     visible meaning search just visible frames,
-     0 meaning search visible and iconified frames,
-     or a window, meaning search the frame that window belongs to.  */
-
-  /* Do this loop at least once, to get the next window, and perhaps
-     again, if we hit the minibuffer and that is not acceptable.  */
-  do
-    {
-      /* Find a window that actually has a next one.  This loop
-        climbs up the tree.  */
-      while (tem = XWINDOW (window)->next, NILP (tem))
-       if (tem = XWINDOW (window)->parent, !NILP (tem))
-         window = tem;
-       else
-         {
-           /* We've reached the end of this frame.
-              Which other frames are acceptable?  */
-           tem = WINDOW_FRAME (XWINDOW (window));
-           if (! NILP (all_frames))
-             {
-               Lisp_Object tem1;
-
-               tem1 = tem;
-               tem = next_frame (tem, all_frames);
-               /* In the case where the minibuffer is active,
-                  and we include its frame as well as the selected one,
-                  next_frame may get stuck in that frame.
-                  If that happens, go back to the selected frame
-                  so we can complete the cycle.  */
-               if (EQ (tem, tem1))
-                 tem = selected_frame;
-             }
-           tem = FRAME_ROOT_WINDOW (XFRAME (tem));
-
-           break;
-         }
-
-      window = tem;
-
-      /* If we're in a combination window, find its first child and
-        recurse on that.  Otherwise, we've found the window we want.  */
-      while (1)
-       {
-         if (!NILP (XWINDOW (window)->hchild))
-           window = XWINDOW (window)->hchild;
-         else if (!NILP (XWINDOW (window)->vchild))
-           window = XWINDOW (window)->vchild;
-         else break;
-       }
-
-      QUIT;
-    }
-  /* Which windows are acceptable?
-     Exit the loop and accept this window if
-     this isn't a minibuffer window,
-     or we're accepting all minibuffer windows,
-     or this is the active minibuffer and we are accepting that one, or
-     we've come all the way around and we're back at the original window.  */
-  while (MINI_WINDOW_P (XWINDOW (window))
-        && ! EQ (minibuf, Qt)
-        && ! EQ (minibuf, window)
-        && ! EQ (window, start_window));
-
-  return window;
+  return next_window (window, minibuf, all_frames, 1);
 }
 
+
 /* This comment supplies the doc string for `previous-window',
    for make-docfile to see.  We cannot put this in the real DEFUN
    due to limits in the Unix cpp.
@@ -1314,128 +1493,12 @@ windows, eventually ending up back at the window you started with.\n\
 DEFUN ("previous-window", Fprevious_window, Sprevious_window, 0, 3, 0,
        0)
   (window, minibuf, all_frames)
-     register Lisp_Object window, minibuf, all_frames;
+     Lisp_Object window, minibuf, all_frames;
 {
-  register Lisp_Object tem;
-  Lisp_Object start_window;
-
-  if (NILP (window))
-    window = selected_window;
-  else
-    CHECK_LIVE_WINDOW (window, 0);
-
-  start_window = window;
-
-  /* minibuf == nil may or may not include minibuffers.
-     Decide if it does.  */
-  if (NILP (minibuf))
-    minibuf = (minibuf_level ? minibuf_window : Qlambda);
-  else if (! EQ (minibuf, Qt))
-    minibuf = Qlambda;
-  /* Now minibuf can be t => count all minibuffer windows,
-     lambda => count none of them,
-     or a specific minibuffer window (the active one) to count.  */
-
-  /* all_frames == nil doesn't specify which frames to include.
-     Decide which frames it includes.  */
-  if (NILP (all_frames))
-    all_frames = (! EQ (minibuf, Qlambda)
-                  ? (FRAME_MINIBUF_WINDOW
-                     (XFRAME
-                      (WINDOW_FRAME
-                       (XWINDOW (window)))))
-                  : Qnil);
-  else if (EQ (all_frames, Qvisible))
-    ;
-  else if (XFASTINT (all_frames) == 0)
-    ;
-  else if (FRAMEP (all_frames) && ! EQ (all_frames, Fwindow_frame (window)))
-    /* If all_frames is a frame and window arg isn't on that frame, just
-       return the first window on the frame.  */
-    return Fframe_first_window (all_frames);
-  else if (! EQ (all_frames, Qt))
-    all_frames = Qnil;
-  /* Now all_frames is t meaning search all frames,
-     nil meaning search just current frame,
-     visible meaning search just visible frames,
-     0 meaning search visible and iconified frames,
-     or a window, meaning search the frame that window belongs to.  */
-
-  /* Do this loop at least once, to get the previous window, and perhaps
-     again, if we hit the minibuffer and that is not acceptable.  */
-  do
-    {
-      /* Find a window that actually has a previous one.  This loop
-        climbs up the tree.  */
-      while (tem = XWINDOW (window)->prev, NILP (tem))
-       if (tem = XWINDOW (window)->parent, !NILP (tem))
-         window = tem;
-       else
-         {
-           /* We have found the top window on the frame.
-              Which frames are acceptable?  */
-           tem = WINDOW_FRAME (XWINDOW (window));
-           if (! NILP (all_frames))
-             /* It's actually important that we use prev_frame here,
-                rather than next_frame.  All the windows acceptable
-                according to the given parameters should form a ring;
-                Fnext_window and Fprevious_window should go back and
-                forth around the ring.  If we use next_frame here,
-                then Fnext_window and Fprevious_window take different
-                paths through the set of acceptable windows.
-                window_loop assumes that these `ring' requirement are
-                met.  */
-             {
-               Lisp_Object tem1;
-
-               tem1 = tem;
-               tem = prev_frame (tem, all_frames);
-               /* In the case where the minibuffer is active,
-                  and we include its frame as well as the selected one,
-                  next_frame may get stuck in that frame.
-                  If that happens, go back to the selected frame
-                  so we can complete the cycle.  */
-               if (EQ (tem, tem1))
-                 tem = selected_frame;
-             }
-           /* If this frame has a minibuffer, find that window first,
-              because it is conceptually the last window in that frame.  */
-           if (FRAME_HAS_MINIBUF_P (XFRAME (tem)))
-             tem = FRAME_MINIBUF_WINDOW (XFRAME (tem));
-           else
-             tem = FRAME_ROOT_WINDOW (XFRAME (tem));
-
-           break;
-         }
-
-      window = tem;
-      /* If we're in a combination window, find its last child and
-        recurse on that.  Otherwise, we've found the window we want.  */
-      while (1)
-       {
-         if (!NILP (XWINDOW (window)->hchild))
-           window = XWINDOW (window)->hchild;
-         else if (!NILP (XWINDOW (window)->vchild))
-           window = XWINDOW (window)->vchild;
-         else break;
-         while (tem = XWINDOW (window)->next, !NILP (tem))
-           window = tem;
-       }
-    }
-  /* Which windows are acceptable?
-     Exit the loop and accept this window if
-     this isn't a minibuffer window,
-     or we're accepting all minibuffer windows,
-     or this is the active minibuffer and we are accepting that one, or
-     we've come all the way around and we're back at the original window.  */
-  while (MINI_WINDOW_P (XWINDOW (window))
-        && ! EQ (minibuf, Qt)
-        && ! EQ (minibuf, window)
-        && ! EQ (window, start_window));
-
-  return window;
+  return next_window (window, minibuf, all_frames, 0);
 }
 
+
 DEFUN ("other-window", Fother_window, Sother_window, 1, 2, "p",
   "Select the ARG'th different window on this frame.\n\
 All windows on current frame are arranged in a cyclic order.\n\
@@ -1443,28 +1506,43 @@ This command selects the window ARG steps away in that order.\n\
 A negative ARG moves in the opposite order.  If the optional second\n\
 argument ALL_FRAMES is non-nil, cycle through all frames.")
   (arg, all_frames)
-     register Lisp_Object arg, all_frames;
+     Lisp_Object arg, all_frames;
 {
-  register int i;
-  register Lisp_Object w;
+  Lisp_Object window;
+  int i;
 
   CHECK_NUMBER (arg, 0);
-  w = selected_window;
-  i = XINT (arg);
-
-  while (i > 0)
-    {
-      w = Fnext_window (w, Qnil, all_frames);
-      i--;
-    }
-  while (i < 0)
-    {
-      w = Fprevious_window (w, Qnil, all_frames);
-      i++;
-    }
-  Fselect_window (w);
+  window = selected_window;
+  
+  for (i = XINT (arg); i > 0; --i)
+    window = Fnext_window (window, Qnil, all_frames);
+  for (; i < 0; ++i)
+    window = Fprevious_window (window, Qnil, all_frames);
+  
+  Fselect_window (window);
   return Qnil;
 }
+
+
+DEFUN ("window-list", Fwindow_list, Swindow_list, 0, 3, 0,
+  "Return a list of windows in canonical ordering.\n\
+Arguments are like for `next-window'.")
+  (window, minibuf, all_frames)
+     Lisp_Object window, minibuf, all_frames;
+{
+  Lisp_Object tail, list;
+
+  decode_next_window_args (&window, &minibuf, &all_frames);
+  list = Qnil;
+  
+  for (tail = window_list (); CONSP (tail); tail = XCDR (tail))
+    if (candidate_window_p (XCAR (tail), window, minibuf, all_frames))
+      list = Fcons (XCAR (tail), list);
+  
+  return Fnreverse (list);
+}
+
+
 \f
 /* Look at all windows, performing an operation specified by TYPE
    with argument OBJ.
@@ -1490,32 +1568,31 @@ enum window_loop
 static Lisp_Object
 window_loop (type, obj, mini, frames)
      enum window_loop type;
-     register Lisp_Object obj, frames;
+     Lisp_Object obj, frames;
      int mini;
 {
-  register Lisp_Object w;
-  register Lisp_Object best_window;
-  register Lisp_Object next_window;
-  register Lisp_Object last_window;
-  FRAME_PTR frame;
-  Lisp_Object frame_arg;
-  frame_arg = Qt;
-
+  Lisp_Object window, windows, best_window, frame_arg;
+  struct frame *f;
+  struct gcpro gcpro1;
+  
   /* If we're only looping through windows on a particular frame,
      frame points to that frame.  If we're looping through windows
      on all frames, frame is 0.  */
   if (FRAMEP (frames))
-    frame = XFRAME (frames);
+    f = XFRAME (frames);
   else if (NILP (frames))
-    frame = SELECTED_FRAME ();
+    f = SELECTED_FRAME ();
   else
-    frame = 0;
-  if (frame)
+    f = NULL;
+  
+  if (f)
     frame_arg = Qlambda;
   else if (XFASTINT (frames) == 0)
     frame_arg = frames;
   else if (EQ (frames, Qvisible))
     frame_arg = frames;
+  else
+    frame_arg = Qt;
 
   /* frame_arg is Qlambda to stick to one frame,
      Qvisible to consider all visible frames,
@@ -1523,11 +1600,11 @@ window_loop (type, obj, mini, frames)
 
   /* Pick a window to start with.  */
   if (WINDOWP (obj))
-    w = obj;
-  else if (frame)
-    w = FRAME_SELECTED_WINDOW (frame);
+    window = obj;
+  else if (f)
+    window = FRAME_SELECTED_WINDOW (f);
   else
-    w = FRAME_SELECTED_WINDOW (SELECTED_FRAME ());
+    window = FRAME_SELECTED_WINDOW (SELECTED_FRAME ());
 
   /* Figure out the last window we're going to mess with.  Since
      Fnext_window, given the same options, is guaranteed to go in a
@@ -1536,178 +1613,162 @@ window_loop (type, obj, mini, frames)
      We can't just wait until we hit the first window again, because
      it might be deleted.  */
 
-  last_window = Fprevious_window (w, mini ? Qt : Qnil, frame_arg);
-
+  windows = Fwindow_list (window, mini ? Qt : Qnil, frame_arg);
+  GCPRO1 (windows);
   best_window = Qnil;
-  for (;;)
+
+  for (; CONSP (windows); windows = CDR (windows))
     {
-      /* Pick the next window now, since some operations will delete
-        the current window.  */
-      next_window = Fnext_window (w, mini ? Qt : Qnil, frame_arg);
-
-      /* Note that we do not pay attention here to whether
-        the frame is visible, since Fnext_window skips non-visible frames
-        if that is desired, under the control of frame_arg.  */
-      if (! MINI_WINDOW_P (XWINDOW (w))
+      struct window *w;
+      
+      window = XCAR (windows);
+      w = XWINDOW (window);
+      
+      /* Note that we do not pay attention here to whether the frame
+        is visible, since Fwindow_list skips non-visible frames if
+        that is desired, under the control of frame_arg.  */
+      if (!MINI_WINDOW_P (w)
          /* For UNSHOW_BUFFER, we must always consider all windows.  */
          || type == UNSHOW_BUFFER
          || (mini && minibuf_level > 0))
        switch (type)
          {
          case GET_BUFFER_WINDOW:
-           if (XBUFFER (XWINDOW (w)->buffer) == XBUFFER (obj)
+           if (EQ (w->buffer, obj)
                /* Don't find any minibuffer window
                   except the one that is currently in use.  */
-               && (MINI_WINDOW_P (XWINDOW (w))
-                   ? EQ (w, minibuf_window) : 1))
-             return w;
+               && (MINI_WINDOW_P (w)
+                   ? EQ (window, minibuf_window)
+                   : 1))
+             {
+               UNGCPRO;
+               return window;
+             }
            break;
 
          case GET_LRU_WINDOW:
            /* t as arg means consider only full-width windows */
-           if (!NILP (obj) && !WINDOW_FULL_WIDTH_P (XWINDOW (w)))
+           if (!NILP (obj) && !WINDOW_FULL_WIDTH_P (w))
              break;
            /* Ignore dedicated windows and minibuffers.  */
-           if (MINI_WINDOW_P (XWINDOW (w))
-               || !NILP (XWINDOW (w)->dedicated))
+           if (MINI_WINDOW_P (w) || !NILP (w->dedicated))
              break;
            if (NILP (best_window)
                || (XFASTINT (XWINDOW (best_window)->use_time)
-                   > XFASTINT (XWINDOW (w)->use_time)))
-             best_window = w;
+                   > XFASTINT (w->use_time)))
+             best_window = window;
            break;
 
          case DELETE_OTHER_WINDOWS:
-           if (XWINDOW (w) != XWINDOW (obj))
-             Fdelete_window (w);
+           if (!EQ (window, obj))
+             Fdelete_window (window);
            break;
 
          case DELETE_BUFFER_WINDOWS:
-           if (EQ (XWINDOW (w)->buffer, obj))
+           if (EQ (w->buffer, obj))
              {
-               FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (w)));
+               struct frame *f = XFRAME (WINDOW_FRAME (w));
 
                /* If this window is dedicated, and in a frame of its own,
                   kill the frame.  */
-               if (EQ (w, FRAME_ROOT_WINDOW (f))
-                   && !NILP (XWINDOW (w)->dedicated)
+               if (EQ (window, FRAME_ROOT_WINDOW (f))
+                   && !NILP (w->dedicated)
                    && other_visible_frames (f))
                  {
                    /* Skip the other windows on this frame.
                       There might be one, the minibuffer!  */
-                   if (! EQ (w, last_window))
-                     while (f == XFRAME (WINDOW_FRAME (XWINDOW (next_window))))
-                       {
-                         /* As we go, check for the end of the loop.
-                            We mustn't start going around a second time.  */
-                         if (EQ (next_window, last_window))
-                           {
-                             last_window = w;
-                             break;
-                           }
-                         next_window = Fnext_window (next_window,
-                                                     mini ? Qt : Qnil,
-                                                     frame_arg);
-                       }
+                   while (CONSP (XCDR (windows))
+                          && EQ (XWINDOW (XCAR (windows))->frame,
+                                 XWINDOW (XCAR (XCDR (windows)))->frame))
+                     windows = XCDR (windows);
+                   
                    /* Now we can safely delete the frame.  */
-                   Fdelete_frame (WINDOW_FRAME (XWINDOW (w)), Qnil);
+                   Fdelete_frame (w->frame, Qnil);
+                 }
+               else if (NILP (w->parent))
+                 {
+                   /* If we're deleting the buffer displayed in the
+                      only window on the frame, find a new buffer to
+                      display there.  */
+                   Lisp_Object buffer;
+                   buffer = Fother_buffer (obj, Qnil, w->frame);
+                   if (NILP (buffer))
+                     buffer = Fget_buffer_create (build_string ("*scratch*"));
+                   Fset_window_buffer (window, buffer);
+                   if (EQ (window, selected_window))
+                     Fset_buffer (w->buffer);
                  }
                else
-                 /* If we're deleting the buffer displayed in the only window
-                    on the frame, find a new buffer to display there.  */
-                 if (NILP (XWINDOW (w)->parent))
-                   {
-                     Lisp_Object new_buffer;
-                     new_buffer = Fother_buffer (obj, Qnil,
-                                                 XWINDOW (w)->frame);
-                     if (NILP (new_buffer))
-                       new_buffer
-                         = Fget_buffer_create (build_string ("*scratch*"));
-                     Fset_window_buffer (w, new_buffer);
-                     if (EQ (w, selected_window))
-                       Fset_buffer (XWINDOW (w)->buffer);
-                   }
-                 else
-                   Fdelete_window (w);
+                 Fdelete_window (window);
              }
            break;
 
          case GET_LARGEST_WINDOW:
-           /* Ignore dedicated windows and minibuffers.  */
-           if (MINI_WINDOW_P (XWINDOW (w))
-               || !NILP (XWINDOW (w)->dedicated)
-               || NILP (best_window))
-             break;
            {
-             struct window *best_window_ptr = XWINDOW (best_window);
-             struct window *w_ptr = XWINDOW (w);
+             struct window *b;
+             
+             /* Ignore dedicated windows and minibuffers.  */
+             if (MINI_WINDOW_P (w)
+                 || !NILP (w->dedicated)
+                 || NILP (best_window))
+               break;
+             
+             b = XWINDOW (best_window);
              if (NILP (best_window)
-                 || (XFASTINT (w_ptr->height) * XFASTINT (w_ptr->width)
-                     > (XFASTINT (best_window_ptr->height)
-                        * XFASTINT (best_window_ptr->width))))
-               best_window = w;
+                 || (XFASTINT (w->height) * XFASTINT (w->width)
+                     > (XFASTINT (b->height) * XFASTINT (b->width))))
+               best_window = window;
            }
            break;
 
          case UNSHOW_BUFFER:
-           if (EQ (XWINDOW (w)->buffer, obj))
+           if (EQ (w->buffer, obj))
              {
+               Lisp_Object buffer;
+               struct frame *f = XFRAME (w->frame);
+               
                /* Find another buffer to show in this window.  */
-               Lisp_Object another_buffer;
-               FRAME_PTR f = XFRAME (WINDOW_FRAME (XWINDOW (w)));
-               another_buffer = Fother_buffer (obj, Qnil, XWINDOW (w)->frame);
-               if (NILP (another_buffer))
-                 another_buffer
-                   = Fget_buffer_create (build_string ("*scratch*"));
+               buffer = Fother_buffer (obj, Qnil, w->frame);
+               if (NILP (buffer))
+                 buffer = Fget_buffer_create (build_string ("*scratch*"));
+               
                /* If this window is dedicated, and in a frame of its own,
                   kill the frame.  */
-               if (EQ (w, FRAME_ROOT_WINDOW (f))
-                   && !NILP (XWINDOW (w)->dedicated)
+               if (EQ (window, FRAME_ROOT_WINDOW (f))
+                   && !NILP (w->dedicated)
                    && other_visible_frames (f))
                  {
                    /* Skip the other windows on this frame.
                       There might be one, the minibuffer!  */
-                   if (! EQ (w, last_window))
-                     while (f == XFRAME (WINDOW_FRAME (XWINDOW (next_window))))
-                       {
-                         /* As we go, check for the end of the loop.
-                            We mustn't start going around a second time.  */
-                         if (EQ (next_window, last_window))
-                           {
-                             last_window = w;
-                             break;
-                           }
-                         next_window = Fnext_window (next_window,
-                                                     mini ? Qt : Qnil,
-                                                     frame_arg);
-                       }
+                   while (CONSP (XCDR (windows))
+                          && EQ (XWINDOW (XCAR (windows))->frame,
+                                 XWINDOW (XCAR (XCDR (windows)))->frame))
+                     windows = XCDR (windows);
+                   
                    /* Now we can safely delete the frame.  */
-                   Fdelete_frame (WINDOW_FRAME (XWINDOW (w)), Qnil);
+                   Fdelete_frame (w->frame, Qnil);
                  }
                else
                  {
                    /* Otherwise show a different buffer in the window.  */
-                   XWINDOW (w)->dedicated = Qnil;
-                   Fset_window_buffer (w, another_buffer);
-                   if (EQ (w, selected_window))
-                     Fset_buffer (XWINDOW (w)->buffer);
+                   w->dedicated = Qnil;
+                   Fset_window_buffer (window, buffer);
+                   if (EQ (window, selected_window))
+                     Fset_buffer (w->buffer);
                  }
              }
            break;
 
            /* Check for a window that has a killed buffer.  */
          case CHECK_ALL_WINDOWS:
-           if (! NILP (XWINDOW (w)->buffer)
-               && NILP (XBUFFER (XWINDOW (w)->buffer)->name))
+           if (! NILP (w->buffer)
+               && NILP (XBUFFER (w->buffer)->name))
              abort ();
+           break;
          }
-
-      if (EQ (w, last_window))
-       break;
-
-      w = next_window;
     }
 
+  UNGCPRO;
   return best_window;
 }
 
@@ -4214,6 +4275,36 @@ redraws with point in the center of the current window.")
 
   return Qnil;
 }
+
+
+/* Value is the number of lines actually displayed in window W,
+   as opposed to its height.  */
+
+static int
+displayed_window_lines (w)
+     struct window *w;
+{
+  struct it it;
+  struct text_pos start;
+  int height = window_box_height (w);
+
+  SET_TEXT_POS_FROM_MARKER (start, w->start);
+  start_display (&it, w, start);
+  move_it_vertically (&it, height);
+
+  /* Add in empty lines at the bottom of the window.  */
+  if (it.current_y < height)
+    {
+      struct frame *f = XFRAME (w->frame);
+      int rest = height - it.current_y;
+      int lines = (rest + CANON_Y_UNIT (f) - 1) / CANON_Y_UNIT (f);
+      it.vpos += lines;
+    }
+  
+  return it.vpos;
+}
+
+
 \f
 DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line,
   1, 1, "P",
@@ -4222,26 +4313,17 @@ With no argument, position point at center of window.\n\
 An argument specifies vertical position within the window;\n\
 zero means top of window, negative means relative to bottom of window.")
   (arg)
-     register Lisp_Object arg;
+     Lisp_Object arg;
 {
-  register struct window *w = XWINDOW (selected_window);
-  register int height = window_internal_height (w);
-  register int start;
+  struct window *w = XWINDOW (selected_window);
+  int lines, start;
   Lisp_Object window;
 
-  if (NILP (arg))
-    XSETFASTINT (arg, height / 2);
-  else
-    {
-      arg = Fprefix_numeric_value (arg);
-      if (XINT (arg) < 0)
-       XSETINT (arg, XINT (arg) + height);
-    }
-
+  window = selected_window;
   start = marker_position (w->start);
-  XSETWINDOW (window, w);
   if (start < BEGV || start > ZV)
     {
+      int height = window_internal_height (w);
       Fvertical_motion (make_number (- (height / 2)), window);
       set_marker_both (w->start, w->buffer, PT, PT_BYTE);
       w->start_at_line_beg = Fbolp ();
@@ -4250,6 +4332,16 @@ zero means top of window, negative means relative to bottom of window.")
   else
     Fgoto_char (w->start);
 
+  lines = displayed_window_lines (w);
+  if (NILP (arg))
+    XSETFASTINT (arg, lines / 2);
+  else
+    {
+      arg = Fprefix_numeric_value (arg);
+      if (XINT (arg) < 0)
+       XSETINT (arg, XINT (arg) + lines);
+    }
+
   return Fvertical_motion (arg, window);
 }
 
@@ -4650,6 +4742,8 @@ delete_all_subwindows (w)
   w->buffer = Qnil;
   w->vchild = Qnil;
   w->hchild = Qnil;
+
+  Vwindow_list = Qnil;
 }
 \f
 static int
@@ -4997,58 +5091,65 @@ non-negative multiple of the canonical character height of WINDOW.")
 \f
 /* Call FN for all leaf windows on frame F.  FN is called with the
    first argument being a pointer to the leaf window, and with
-   additional arguments A1..A4.  */
+   additional argument USER_DATA.  Stops when FN returns 0.  */
 
 void
-foreach_window (f, fn, a1, a2, a3, a4)
+foreach_window (f, fn, user_data)
      struct frame *f;
-     void (* fn) ();
-     int a1, a2, a3, a4;
+     int (* fn) P_ ((struct window *, void *));
+     void *user_data;
 {
-  foreach_window_1 (XWINDOW (FRAME_ROOT_WINDOW (f)), fn, a1, a2, a3, a4);
+  foreach_window_1 (XWINDOW (FRAME_ROOT_WINDOW (f)), fn, user_data);
 }
 
 
 /* Helper function for foreach_window.  Call FN for all leaf windows
    reachable from W.  FN is called with the first argument being a
-   pointer to the leaf window, and with additional arguments A1..A4.  */
+   pointer to the leaf window, and with additional argument USER_DATA.
+   Stop when FN returns 0.  Value is 0 if stopped by FN.  */
 
-static void
-foreach_window_1 (w, fn, a1, a2, a3, a4)
+static int
+foreach_window_1 (w, fn, user_data)
      struct window *w;
-     void (* fn) ();
-     int a1, a2, a3, a4;
+     int (* fn) P_ ((struct window *, void *));
+     void *user_data;
 {
-  while (w)
+  int cont;
+  
+  for (cont = 1; w && cont;)
     {
       if (!NILP (w->hchild))
-       foreach_window_1 (XWINDOW (w->hchild), fn, a1, a2, a3, a4);
+       cont = foreach_window_1 (XWINDOW (w->hchild), fn, user_data);
       else if (!NILP (w->vchild))
-       foreach_window_1 (XWINDOW (w->vchild), fn, a1, a2, a3, a4);
-      else
-       fn (w, a1, a2, a3, a4);
+       cont = foreach_window_1 (XWINDOW (w->vchild), fn, user_data);
+      else 
+       cont = fn (w, user_data);
       
       w = NILP (w->next) ? 0 : XWINDOW (w->next);
     }
+
+  return cont;
 }
 
 
 /* Freeze or unfreeze the window start of W if unless it is a
-   mini-window or the selected window.  FREEZE_P non-zero means freeze
+   mini-window or the selected window.  FREEZE_P non-null means freeze
    the window start.  */
 
-static void
+static int
 freeze_window_start (w, freeze_p)
      struct window *w;
-     int freeze_p;
+     void *freeze_p;
 {
   if (w == XWINDOW (selected_window)
       || MINI_WINDOW_P (w)
       || (MINI_WINDOW_P (XWINDOW (selected_window))
+         && ! NILP (Vminibuf_scroll_window)
          && w == XWINDOW (Vminibuf_scroll_window)))
-    freeze_p = 0;
+    freeze_p = NULL;
   
-  w->frozen_window_start_p = freeze_p;
+  w->frozen_window_start_p = freeze_p != NULL;
+  return 1;
 }
 
 
@@ -5061,7 +5162,7 @@ freeze_window_starts (f, freeze_p)
      struct frame *f;
      int freeze_p;
 {
-  foreach_window (f, freeze_window_start, freeze_p);
+  foreach_window (f, freeze_window_start, (void *) freeze_p);
 }
 
 \f
@@ -5199,6 +5300,12 @@ init_window_once ()
   window_initialized = 1;
 }
 
+void
+init_window ()
+{
+  Vwindow_list = Qnil;
+}
+
 void
 syms_of_window ()
 {
@@ -5226,6 +5333,8 @@ syms_of_window ()
   Qtemp_buffer_show_hook = intern ("temp-buffer-show-hook");
   staticpro (&Qtemp_buffer_show_hook);
 
+  staticpro (&Vwindow_list);
+
   DEFVAR_LISP ("temp-buffer-show-function", &Vtemp_buffer_show_function,
     "Non-nil means call as function to display a help buffer.\n\
 The function is called with one argument, the buffer to be displayed.\n\
@@ -5433,6 +5542,7 @@ The selected frame is the one whose configuration has changed.");
   defsubr (&Swindow_vscroll);
   defsubr (&Sset_window_vscroll);
   defsubr (&Scompare_window_configurations);
+  defsubr (&Swindow_list);
 }
 
 void