(fix_submap_inheritance, get_keyelt, store_in_keymap,
[bpt/emacs.git] / src / window.c
index 61c7173..1f4db42 100644 (file)
@@ -1,6 +1,6 @@
 /* Window creation, deletion and examination for GNU Emacs.
    Does not include redisplay.
-   Copyright (C) 1985, 86, 87, 93, 94, 95, 96 Free Software Foundation, Inc.
+   Copyright (C) 1985,86,87,93,94,95,96,97,1998 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -29,14 +29,11 @@ Boston, MA 02111-1307, USA.  */
 #include "termchar.h"
 #include "disptab.h"
 #include "keyboard.h"
+#include "blockinput.h"
 
 Lisp_Object Qwindowp, Qwindow_live_p;
 
-Lisp_Object Fnext_window (), Fdelete_window (), Fselect_window ();
-Lisp_Object Fset_window_buffer (), Fsplit_window (), Frecenter ();
-
-void delete_all_subwindows ();
-static struct window *decode_window();
+static struct window *decode_window P_ ((Lisp_Object));
 
 /* This is the window in which the terminal's cursor should
    be left when nothing is being done with it.  This must
@@ -111,8 +108,21 @@ static int sequence_number;
 /* Nonzero after init_window_once has finished.  */
 static int window_initialized;
 
+/* Hook to run when window config changes.  */
+Lisp_Object Qwindow_configuration_change_hook;
+Lisp_Object Vwindow_configuration_change_hook;
+
+/* Nonzero means scroll commands try to put point
+   at the same screen height as previously.  */
+static int scroll_preserve_screen_position;
+
+/* Nonzero means we can split a frame even if it is "unsplittable".  */
+static int inhibit_frame_unsplittable;
+
 #define min(a, b) ((a) < (b) ? (a) : (b))
 
+extern int scroll_margin;
+
 extern Lisp_Object Qwindow_scroll_functions, Vwindow_scroll_functions;
 \f
 DEFUN ("windowp", Fwindowp, Swindowp, 1, 1, 0,
@@ -247,8 +257,17 @@ POS defaults to point; WINDOW, to the selected window.")
        return Qnil;
 
       /* If that info is not correct, calculate afresh */
+      /* BUG FIX for the 7th arg (TOHPOS).
+
+        '0' is harmless, however, ' - (1 << (BITS_PER_SHORT - 1))' is
+        more appropriate here.  In case of HSCROLL > 0, this can avoid
+        needless calculation done until (HPOS == 0).
+
+        We want to determine if the position POSINT is in HEIGHT or
+        not.  We don't have to do calculation until (HPOS == 0).  We
+        can stop it when VPOS goes beyond HEIGHT.  */
       posval = *compute_motion (top, 0, (hscroll ? 1 - hscroll : 0), 0,
-                               posint, height, 0,
+                               posint, height, - (1 << (BITS_PER_SHORT - 1)),
                                window_internal_width (w) - 1,
                                hscroll, 0, w);
 
@@ -434,7 +453,7 @@ If they are on the border between WINDOW and its right sibling,\n\
       return Qnil;
 
     case 1:                    /* In text part of window. */
-      return Fcons (x, y);
+      return Fcons (make_number (x), make_number (y));
 
     case 2:                    /* In mode line of window. */
       return Qmode_line;
@@ -542,13 +561,15 @@ have been if redisplay had finished, do this:\n\
       (vertical-motion (1- (window-height window)) window)\n\
       (point))")  */
 
-DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 1, 0,
+DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0,
   "Return position at which display currently ends in WINDOW.\n\
 This is updated by redisplay, when it runs to completion.\n\
 Simply changing the buffer text or setting `window-start'\n\
-does not update this value.")
-  (window)
-     Lisp_Object window;
+does not update this value.\n\
+If UP-TO-DATE is non-nil, compute the up-to-date position\n\
+if it isn't already recorded.")
+  (window, update)
+     Lisp_Object window, update;
 {
   Lisp_Object value;
   struct window *w = decode_window (window);
@@ -566,8 +587,20 @@ does not update this value.")
     return Qnil;
 #endif
 
-  XSETINT (value,
-          BUF_Z (XBUFFER (buf)) - XFASTINT (w->window_end_pos));
+  if (! NILP (update)
+      && ! (! NILP (w->window_end_valid)
+           && XFASTINT (w->last_modified) >= MODIFF))
+    {
+      int opoint = PT, opoint_byte = PT_BYTE;
+      TEMP_SET_PT_BOTH (XMARKER (w->start)->charpos,
+                       XMARKER (w->start)->bytepos);
+      Fvertical_motion (make_number (window_internal_height (w)), Qnil);
+      XSETINT (value, PT);
+      TEMP_SET_PT_BOTH (opoint, opoint_byte);
+    }
+  else
+    XSETINT (value,
+            BUF_Z (XBUFFER (buf)) - XFASTINT (w->window_end_pos));
 
   return value;
 }
@@ -663,6 +696,9 @@ window_display_table (w)
   tem = w->display_table;
   if (DISP_TABLE_P (tem))
     return XCHAR_TABLE (tem);
+  if (NILP (w->buffer))
+    return 0;
+
   tem = XBUFFER (w->buffer)->display_table;
   if (DISP_TABLE_P (tem))
     return XCHAR_TABLE (tem);
@@ -692,13 +728,15 @@ unshow_buffer (w)
      register struct window *w;
 {
   Lisp_Object buf;
+  struct buffer *b;
 
   buf = w->buffer;
-  if (XBUFFER (buf) != XMARKER (w->pointm)->buffer)
+  b = XBUFFER (buf);
+  if (b != XMARKER (w->pointm)->buffer)
     abort ();
 
-  if (w == XWINDOW (XBUFFER (buf)->last_selected_window))
-    XBUFFER (buf)->last_selected_window = Qnil;
+  if (w == XWINDOW (b->last_selected_window))
+    b->last_selected_window = Qnil;
 
 #if 0
   if (w == XWINDOW (selected_window)
@@ -713,16 +751,19 @@ unshow_buffer (w)
        selected window, while last_window_start reflects another
        window which was recently showing the same buffer.
        Some people might say that might be a good thing.  Let's see.  */
-    XBUFFER (buf)->last_window_start = marker_position (w->start);
+    b->last_window_start = marker_position (w->start);
 
   /* Point in the selected window's buffer
      is actually stored in that buffer, and the window's pointm isn't used.
      So don't clobber point in that buffer.  */
   if (! EQ (buf, XWINDOW (selected_window)->buffer))
-    BUF_PT (XBUFFER (buf))
-      = clip_to_bounds (BUF_BEGV (XBUFFER (buf)),
-                       marker_position (w->pointm),
-                       BUF_ZV (XBUFFER (buf)));
+    temp_set_point_both (b,
+                        clip_to_bounds (BUF_BEGV (b),
+                                        XMARKER (w->pointm)->charpos,
+                                        BUF_ZV (b)),
+                        clip_to_bounds (BUF_BEGV_BYTE (b),
+                                        marker_byte_position (w->pointm),
+                                        BUF_ZV_BYTE (b)));
 }
 
 /* Put replacement into the window structure in place of old. */
@@ -770,6 +811,19 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
   "Remove WINDOW from the display.  Default is selected window.")
   (window)
      register Lisp_Object window;
+{
+  delete_window (window);
+
+  if (! NILP (Vwindow_configuration_change_hook)
+      && ! NILP (Vrun_hooks))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
+  return Qnil;
+}
+
+void
+delete_window (window)
+     register Lisp_Object window;
 {
   register Lisp_Object tem, parent, sib;
   register struct window *p;
@@ -788,7 +842,7 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
   if (NILP (p->buffer)
       && NILP (p->hchild)
       && NILP (p->vchild))
-    return Qnil;
+    return;
 
   parent = p->parent;
   if (NILP (parent))
@@ -895,8 +949,6 @@ DEFUN ("delete-window", Fdelete_window, Sdelete_window, 0, 1, "",
 
   /* Mark this window as deleted.  */
   p->buffer = p->hchild = p->vchild = Qnil;
-
-  return Qnil;
 }
 \f
 
@@ -1319,7 +1371,11 @@ window_loop (type, obj, mini, frames)
        switch (type)
          {
          case GET_BUFFER_WINDOW:
-           if (XBUFFER (XWINDOW (w)->buffer) == XBUFFER (obj))
+           if (XBUFFER (XWINDOW (w)->buffer) == XBUFFER (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;
            break;
 
@@ -1560,13 +1616,14 @@ value is reasonable when this function is called.")
       /* This computation used to temporarily move point, but that can
         have unwanted side effects due to text properties.  */
       pos = *vmotion (startpos, -top, w);
-      Fset_marker (w->start, make_number (pos.bufpos), w->buffer);
-      w->start_at_line_beg = ((pos.bufpos == BEGV
-                              || FETCH_CHAR (pos.bufpos - 1) == '\n') ? Qt
+
+      set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
+      w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
+                              || FETCH_BYTE (pos.bytepos - 1) == '\n') ? Qt
                              : Qnil);
       /* We need to do this, so that the window-scroll-functions
         get called.  */
-      w->force_start = Qt;
+      w->optional_new_start = Qt;
 
       set_buffer_internal (obuf);
     }
@@ -1628,9 +1685,9 @@ replace_buffer_in_all_windows (buffer)
      because it only considers frames on the current keyboard.
      So loop manually over frames, and handle each one.  */
   FOR_EACH_FRAME (tail, frame)
-    window_loop (UNSHOW_BUFFER, buffer, 0, frame);
+    window_loop (UNSHOW_BUFFER, buffer, 1, frame);
 #else
-  window_loop (UNSHOW_BUFFER, buffer, 0, Qt);
+  window_loop (UNSHOW_BUFFER, buffer, 1, Qt);
 #endif
 }
 \f
@@ -1682,6 +1739,7 @@ check_frame_size (frame, rows, cols)
    nodelete nonzero means do not do this.
    (The caller should check later and do so if appropriate)  */
 
+void
 set_window_height (window, height, nodelete)
      Lisp_Object window;
      int height;
@@ -1697,9 +1755,11 @@ set_window_height (window, height, nodelete)
 
   if (!nodelete
       && ! NILP (w->parent)
-      && height < window_min_height)
+      && (MINI_WINDOW_P (w)
+         ? height < 1
+         : height < window_min_height))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
@@ -1750,6 +1810,7 @@ set_window_height (window, height, nodelete)
 
 /* Recursively set width of WINDOW and its inferiors. */
 
+void
 set_window_width (window, width, nodelete)
      Lisp_Object window;
      int width;
@@ -1763,7 +1824,7 @@ set_window_width (window, width, nodelete)
 
   if (!nodelete && width < window_min_width && !NILP (w->parent))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
@@ -1854,13 +1915,15 @@ BUFFER can be a buffer or buffer name.")
 
   if (EQ (window, selected_window))
     XBUFFER (w->buffer)->last_selected_window = window;
+  if (INTEGERP (XBUFFER (buffer)->display_count))
+    XSETINT (XBUFFER (buffer)->display_count,
+            XINT (XBUFFER (buffer)->display_count) + 1);
 
   XSETFASTINT (w->window_end_pos, 0);
   w->window_end_valid = Qnil;
   XSETFASTINT (w->hscroll, 0);
-  Fset_marker (w->pointm,
-              make_number (BUF_PT (XBUFFER (buffer))),
-              buffer);
+  set_marker_both (w->pointm, buffer,
+                  BUF_PT (XBUFFER (buffer)), BUF_PT_BYTE (XBUFFER (buffer)));
   set_marker_restricted (w->start,
                         make_number (XBUFFER (buffer)->last_window_start),
                         buffer);
@@ -1887,6 +1950,10 @@ BUFFER can be a buffer or buffer name.")
     run_hook_with_args_2 (Qwindow_scroll_functions, window,
                          Fmarker_position (w->start));
 
+  if (! NILP (Vwindow_configuration_change_hook)
+      && ! NILP (Vrun_hooks))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   unbind_to (count, Qnil);
 
   return Qnil;
@@ -1913,8 +1980,10 @@ before each command.")
   if (EQ (window, selected_window))
     return window;
 
-  Fset_marker (ow->pointm, make_number (BUF_PT (XBUFFER (ow->buffer))),
-              ow->buffer);
+  if (! NILP (ow->buffer))
+    set_marker_both (ow->pointm, ow->buffer,
+                    BUF_PT (XBUFFER (ow->buffer)),
+                    BUF_PT_BYTE (XBUFFER (ow->buffer)));
 
   selected_window = window;
   if (XFRAME (WINDOW_FRAME (w)) != selected_frame)
@@ -1980,9 +2049,9 @@ display_buffer_1 (window)
 }
 
 DEFUN ("special-display-p", Fspecial_display_p, Sspecial_display_p, 1, 1, 0,
-  "Returns non-nil if a buffer named BUFFER-NAME would be created specially.
-The value is actually t if the frame should be called with default frame
-parameters, and a list of frame parameters if they were specified.
+  "Returns non-nil if a buffer named BUFFER-NAME would be created specially.\n\
+The value is actually t if the frame should be called with default frame\n\
+parameters, and a list of frame parameters if they were specified.\n\
 See `special-display-buffer-names', and `special-display-regexps'.")
   (buffer_name)
      Lisp_Object buffer_name;
@@ -2008,13 +2077,13 @@ See `special-display-buffer-names', and `special-display-regexps'.")
       else if (CONSP (car)
               && STRINGP (XCAR (car))
               && fast_string_match (XCAR (car), buffer_name) >= 0)
-       return XCDR (tem);
+       return XCDR (car);
     }
   return Qnil;
 }  
 
 DEFUN ("same-window-p", Fsame_window_p, Ssame_window_p, 1, 1, 0,
-  "Returns non-nil if a new buffer named BUFFER-NAME would use the same window.
+  "Returns non-nil if a new buffer named BUFFER-NAME would use the same window.\n\
 See `same-window-buffer-names' and `same-window-regexps'.")
   (buffer_name)
      Lisp_Object buffer_name;
@@ -2045,8 +2114,9 @@ See `same-window-buffer-names' and `same-window-regexps'.")
   return Qnil;
 }
 
-DEFUN ("display-buffer", Fdisplay_buffer, Sdisplay_buffer, 1, 2,
-       "bDisplay buffer: \nP",
+   /* Use B so the default is (other-buffer).  */
+DEFUN ("display-buffer", Fdisplay_buffer, Sdisplay_buffer, 1, 3,
+     "BDisplay buffer: \nP",
   "Make BUFFER appear in some window but don't select it.\n\
 BUFFER can be a buffer or a buffer name.\n\
 If BUFFER is shown already in some window, just use that one,\n\
@@ -2057,9 +2127,18 @@ Returns the window displaying BUFFER.\n\
 \n\
 The variables `special-display-buffer-names', `special-display-regexps',\n\
 `same-window-buffer-names', and `same-window-regexps' customize how certain\n\
-buffer names are handled.")
-  (buffer, not_this_window)
-     register Lisp_Object buffer, not_this_window;
+buffer names are handled.\n\
+\n\
+If optional argument FRAME is `visible', search all visible frames.\n\
+If FRAME is 0, search all visible and iconified frames.\n\
+If FRAME is t, search all frames.\n\
+If FRAME is a frame, search only that frame.\n\
+If FRAME is nil, search only the selected frame\n\
+ (actually the last nonminibuffer frame),\n\
+ unless `pop-up-frames' is non-nil,\n\
+ which means search visible and iconified frames.")
+  (buffer, not_this_window, frame)
+     register Lisp_Object buffer, not_this_window, frame;
 {
   register Lisp_Object window, tem;
 
@@ -2088,7 +2167,9 @@ buffer names are handled.")
   /* If pop_up_frames,
      look for a window showing BUFFER on any visible or iconified frame.
      Otherwise search only the current frame.  */
-  if (pop_up_frames || last_nonminibuf_frame == 0)
+  if (! NILP (frame))
+    tem = frame;
+  else if (pop_up_frames || last_nonminibuf_frame == 0)
     XSETFASTINT (tem, 0);
   else
     XSETFRAME (tem, last_nonminibuf_frame);
@@ -2175,13 +2256,20 @@ buffer names are handled.")
              && window_height (window) >= window_min_height << 1)
            window = Fsplit_window (window, Qnil, Qnil);
          /* If Fget_lru_window returned nil, try other approaches.  */
+
          /* Try visible frames first.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, Qvisible);
          if (NILP (window))
            window = Fget_largest_window (Qvisible);
          /* If that didn't work, try iconified frames.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, make_number (0));
          if (NILP (window))
            window = Fget_largest_window (make_number (0));
          /* Try invisible frames.  */
+         if (NILP (window))
+           window = Fget_buffer_window (buffer, Qt);
          if (NILP (window))
            window = Fget_largest_window (Qt);
          /* As a last resort, make a new frame.  */
@@ -2196,15 +2284,19 @@ buffer names are handled.")
            other = lower = XWINDOW (window)->next, upper = window;
          if (!NILP (other)
              /* Check that OTHER and WINDOW are vertically arrayed.  */
-             && XWINDOW (other)->top != XWINDOW (window)->top
-             && XWINDOW (other)->height > XWINDOW (window)->height)
+             && !EQ (XWINDOW (other)->top, XWINDOW (window)->top)
+             && (XFASTINT (XWINDOW (other)->height)
+                 > XFASTINT (XWINDOW (window)->height)))
            {
-             int total = XWINDOW (other)->height + XWINDOW (window)->height;
+             int total = (XFASTINT (XWINDOW (other)->height)
+                          + XFASTINT (XWINDOW (window)->height));
              Lisp_Object old_selected_window;
              old_selected_window = selected_window;
 
              selected_window = upper;
-             change_window_height (total / 2 - XWINDOW (upper)->height, 0);
+             change_window_height ((total / 2
+                                    - XFASTINT (XWINDOW (upper)->height)),
+                                   0);
              selected_window = old_selected_window;
            }
        }
@@ -2224,6 +2316,8 @@ temp_output_buffer_show (buf)
   register Lisp_Object window;
   register struct window *w;
 
+  XBUFFER (buf)->directory = current_buffer->directory;
+
   Fset_buffer (buf);
   BUF_SAVE_MODIFF (XBUFFER (buf)) = MODIFF;
   BEGV = BEG;
@@ -2236,15 +2330,15 @@ temp_output_buffer_show (buf)
     call1 (Vtemp_buffer_show_function, buf);
   else
     {
-      window = Fdisplay_buffer (buf, Qnil);
+      window = Fdisplay_buffer (buf, Qnil, Qnil);
 
       if (XFRAME (XWINDOW (window)->frame) != selected_frame)
        Fmake_frame_visible (WINDOW_FRAME (XWINDOW (window)));
       Vminibuf_scroll_window = window;
       w = XWINDOW (window);
       XSETFASTINT (w->hscroll, 0);
-      set_marker_restricted (w->start, make_number (1), buf);
-      set_marker_restricted (w->pointm, make_number (1), buf);
+      set_marker_restricted_both (w->start, buf, 1, 1);
+      set_marker_restricted_both (w->pointm, buf, 1, 1);
 
       /* Run temp-buffer-show-hook, with the chosen window selected.  */
       if (!NILP (Vrun_hooks))
@@ -2342,8 +2436,6 @@ and put SIZE columns in the first of the pair.")
 
   if (MINI_WINDOW_P (o))
     error ("Attempt to split minibuffer window");
-  else if (FRAME_NO_SPLIT_P (fo))
-    error ("Attempt to split unsplittable frame");
 
   check_min_window_sizes ();
 
@@ -2397,8 +2489,6 @@ and put SIZE columns in the first of the pair.")
   p->parent = o->parent;
   p->buffer = Qt;
 
-  Fset_window_buffer (new, o->buffer);
-
   /* Apportion the available frame space among the two new windows */
 
   if (!NILP (horflag))
@@ -2418,6 +2508,8 @@ and put SIZE columns in the first of the pair.")
       XSETFASTINT (p->top, XFASTINT (o->top) + size_int);
     }
 
+  Fset_window_buffer (new, o->buffer);
+
   return new;
 }
 \f
@@ -2429,6 +2521,10 @@ From program, optional second arg non-nil means grow sideways ARG columns.")
 {
   CHECK_NUMBER (arg, 0);
   change_window_height (XINT (arg), !NILP (side));
+
+  if (! NILP (Vwindow_configuration_change_hook))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   return Qnil;
 }
 
@@ -2440,6 +2536,10 @@ From program, optional second arg non-nil means shrink sideways arg columns.")
 {
   CHECK_NUMBER (arg, 0);
   change_window_height (-XINT (arg), !NILP (side));
+
+  if (! NILP (Vwindow_configuration_change_hook))
+    call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+
   return Qnil;
 }
 
@@ -2474,6 +2574,7 @@ window_width (window)
    also changes the heights of the siblings so as to
    keep everything consistent. */
 
+void
 change_window_height (delta, widthflag)
      register int delta;
      int widthflag;
@@ -2482,10 +2583,12 @@ change_window_height (delta, widthflag)
   Lisp_Object window;
   register struct window *p;
   int *sizep;
-  int (*sizefun) () = widthflag ? window_width : window_height;
-  register int (*setsizefun) () = (widthflag
-                                  ? set_window_width
-                                  : set_window_height);
+  int (*sizefun) P_ ((Lisp_Object))
+    = widthflag ? window_width : window_height;
+  register void (*setsizefun) P_ ((Lisp_Object, int, int))
+    = (widthflag ? set_window_width : set_window_height);
+  int maximum;
+  Lisp_Object next, prev;
 
   check_min_window_sizes ();
 
@@ -2527,29 +2630,78 @@ change_window_height (delta, widthflag)
 
   if (*sizep + delta < MINSIZE (window))
     {
-      Fdelete_window (window);
+      delete_window (window);
       return;
     }
 
   if (delta == 0)
     return;
 
-  if (!NILP (p->next)
-      && (*sizefun) (p->next) - delta >= MINSIZE (p->next))
-    {
-      (*setsizefun) (p->next, (*sizefun) (p->next) - delta, 0);
-      (*setsizefun) (window, *sizep + delta, 0);
-      CURBEG (p->next) += delta;
-      /* This does not change size of p->next,
-        but it propagates the new top edge to its children */
-      (*setsizefun) (p->next, (*sizefun) (p->next), 0);
-    }
-  else if (!NILP (p->prev)
-          && (*sizefun) (p->prev) - delta >= MINSIZE (p->prev))
+  /* Find the total we can get from other siblings.  */
+  maximum = 0;
+  for (next = p->next; ! NILP (next); next = XWINDOW (next)->next)
+    maximum += (*sizefun) (next) - MINSIZE (next);
+  for (prev = p->prev; ! NILP (prev); prev = XWINDOW (prev)->prev)
+    maximum += (*sizefun) (prev) - MINSIZE (prev);
+
+  /* If we can get it all from them, do so.  */
+  if (delta <= maximum)
     {
-      (*setsizefun) (p->prev, (*sizefun) (p->prev) - delta, 0);
-      CURBEG (window) -= delta;
-      (*setsizefun) (window, *sizep + delta, 0);
+      Lisp_Object first_unaffected;
+      Lisp_Object first_affected;
+
+      next = p->next;
+      prev = p->prev;
+      first_affected = window;
+      /* Look at one sibling at a time,
+        moving away from this window in both directions alternately,
+        and take as much as we can get without deleting that sibling.  */
+      while (delta != 0)
+       {
+         if (delta == 0)
+           break;
+         if (! NILP (next))
+           {
+             int this_one = (*sizefun) (next) - MINSIZE (next);
+             if (this_one > delta)
+               this_one = delta;
+
+             (*setsizefun) (next, (*sizefun) (next) - this_one, 0);
+             (*setsizefun) (window, *sizep + this_one, 0);
+
+             delta -= this_one;
+             next = XWINDOW (next)->next;
+           }
+         if (delta == 0)
+           break;
+         if (! NILP (prev))
+           {
+             int this_one = (*sizefun) (prev) - MINSIZE (prev);
+             if (this_one > delta)
+               this_one = delta;
+
+             first_affected = prev;
+
+             (*setsizefun) (prev, (*sizefun) (prev) - this_one, 0);
+             (*setsizefun) (window, *sizep + this_one, 0);
+
+             delta -= this_one;
+             prev = XWINDOW (prev)->prev;
+           }
+       }
+
+      /* Now recalculate the edge positions of all the windows affected,
+        based on the new sizes.  */
+      first_unaffected = next;
+      prev = first_affected;
+      for (next = XWINDOW (prev)->next; ! EQ (next, first_unaffected);
+          prev = next, next = XWINDOW (next)->next)
+       {
+         CURBEG (next) = CURBEG (prev) + (*sizefun) (prev);
+         /* This does not change size of NEXT,
+            but it propagates the new top edge to its children */
+         (*setsizefun) (next, (*sizefun) (next), 0);
+       }
     }
   else
     {
@@ -2632,25 +2784,35 @@ window_internal_width (w)
 }
 
 
-/* Scroll contents of window WINDOW up N lines.  */
+/* Scroll contents of window WINDOW up N lines.
+   If WHOLE is nonzero, it means scroll N screenfuls instead.  */
 
-void
-window_scroll (window, n, noerror)
+static void
+window_scroll (window, n, whole, noerror)
      Lisp_Object window;
      int n;
+     int whole;
      int noerror;
 {
   register struct window *w = XWINDOW (window);
   register int opoint = PT;
-  register int pos;
+  register int opoint_byte = PT_BYTE;
+  register int pos, pos_byte;
   register int ht = window_internal_height (w);
   register Lisp_Object tem;
   int lose;
   Lisp_Object bolp, nmoved;
+  int startpos;
+  struct position posit;
+  int original_vpos;
 
-  /* Always set force_start so that redisplay_window will run
-     the window-scroll-functions.  */
-  w->force_start = Qt;
+  startpos = marker_position (w->start);
+
+  posit = *compute_motion (startpos, 0, 0, 0,
+                          PT, ht, 0,
+                          window_internal_width (w), XINT (w->hscroll),
+                          0, w);
+  original_vpos = posit.vpos;
 
   XSETFASTINT (tem, PT);
   tem = Fpos_visible_in_window_p (tem, window);
@@ -2658,16 +2820,16 @@ window_scroll (window, n, noerror)
   if (NILP (tem))
     {
       Fvertical_motion (make_number (- (ht / 2)), window);
-      XSETFASTINT (tem, PT);
-      Fset_marker (w->start, tem, w->buffer);
+      startpos = PT;
     }
 
-  SET_PT (marker_position (w->start));
+  SET_PT (startpos);
   lose = n < 0 && PT == BEGV;
   Fvertical_motion (make_number (n), window);
   pos = PT;
+  pos_byte = PT_BYTE;
   bolp = Fbolp ();
-  SET_PT (opoint);
+  SET_PT_BOTH (opoint, opoint_byte);
 
   if (lose)
     {
@@ -2679,21 +2841,79 @@ window_scroll (window, n, noerror)
 
   if (pos < ZV)
     {
-      set_marker_restricted (w->start, make_number (pos), w->buffer);
+      int this_scroll_margin = scroll_margin;
+
+      /* Don't use a scroll margin that is negative or too large.  */
+      if (this_scroll_margin < 0)
+       this_scroll_margin = 0;
+
+      if (XINT (w->height) < 4 * scroll_margin)
+       this_scroll_margin = XINT (w->height) / 4;
+
+      set_marker_restricted_both (w->start, w->buffer, pos, pos_byte);
       w->start_at_line_beg = bolp;
       w->update_mode_line = Qt;
       XSETFASTINT (w->last_modified, 0);
       XSETFASTINT (w->last_overlay_modified, 0);
-      if (pos > opoint)
-       SET_PT (pos);
-      if (n < 0)
+      /* Set force_start so that redisplay_window will run
+        the window-scroll-functions.  */
+      w->force_start = Qt;
+
+      if (whole && scroll_preserve_screen_position)
+       {
+         SET_PT_BOTH (pos, pos_byte);
+         Fvertical_motion (make_number (original_vpos), window);
+       }
+      /* If we scrolled forward, put point enough lines down
+        that it is outside the scroll margin.  */
+      else if (n > 0)
+       {
+         int top_margin;
+
+         if (this_scroll_margin > 0)
+           {
+             SET_PT_BOTH (pos, pos_byte);
+             Fvertical_motion (make_number (this_scroll_margin), window);
+             top_margin = PT;
+           }
+         else
+           top_margin = pos;
+
+         if (top_margin <= opoint)
+           SET_PT_BOTH (opoint, opoint_byte);
+         else if (scroll_preserve_screen_position)
+           {
+             SET_PT_BOTH (pos, pos_byte);
+             Fvertical_motion (make_number (original_vpos), window);
+           }
+         else
+           SET_PT (top_margin);
+       }
+      else if (n < 0)
        {
-         SET_PT (pos);
-         tem = Fvertical_motion (make_number (ht), window);
-         if (PT > opoint || XFASTINT (tem) < ht)
-           SET_PT (opoint);
+         int bottom_margin;
+
+         /* If we scrolled backward, put point near the end of the window
+            but not within the scroll margin.  */
+         SET_PT_BOTH (pos, pos_byte);
+         tem = Fvertical_motion (make_number (ht - this_scroll_margin), window);
+         if (XFASTINT (tem) == ht - this_scroll_margin)
+           bottom_margin = PT;
+         else
+           bottom_margin = PT + 1;
+
+         if (bottom_margin > opoint)
+           SET_PT_BOTH (opoint, opoint_byte);
          else
-           Fvertical_motion (make_number (-1), window);
+           {
+             if (scroll_preserve_screen_position)
+               {
+                 SET_PT_BOTH (pos, pos_byte);
+                 Fvertical_motion (make_number (original_vpos), window);
+               }
+             else
+               Fvertical_motion (make_number (-1), window);
+           }
        }
     }
   else
@@ -2728,13 +2948,13 @@ scroll_command (n, direction)
   defalt = direction * (defalt < 1 ? 1 : defalt);
 
   if (NILP (n))
-    window_scroll (selected_window, defalt, 0);
+    window_scroll (selected_window, defalt, 1, 0);
   else if (EQ (n, Qminus))
-    window_scroll (selected_window, - defalt, 0);
+    window_scroll (selected_window, - defalt, 1, 0);
   else
     {
       n = Fprefix_numeric_value (n);
-      window_scroll (selected_window, XINT (n) * direction, 0);
+      window_scroll (selected_window, XINT (n) * direction, 0, 0);
     }
 
   unbind_to (count, Qnil);
@@ -2782,7 +3002,7 @@ showing that buffer is used.")
     {
       window = Fget_buffer_window (Vother_window_scroll_buffer, Qnil);
       if (NILP (window))
-       window = Fdisplay_buffer (Vother_window_scroll_buffer, Qt);
+       window = Fdisplay_buffer (Vother_window_scroll_buffer, Qt, Qnil);
     }
   else
     {
@@ -2838,18 +3058,18 @@ showing that buffer, popping the buffer up if necessary.")
   SET_PT (marker_position (w->pointm));
 
   if (NILP (arg))
-    window_scroll (window, defalt, 1);
+    window_scroll (window, defalt, 1, 1);
   else if (EQ (arg, Qminus))
-    window_scroll (window, -defalt, 1);
+    window_scroll (window, -defalt, 1, 1);
   else
     {
       if (CONSP (arg))
        arg = Fcar (arg);
       CHECK_NUMBER (arg, 0);
-      window_scroll (window, XINT (arg), 1);
+      window_scroll (window, XINT (arg), 0, 1);
     }
 
-  Fset_marker (w->pointm, make_number (PT), Qnil);
+  set_marker_both (w->pointm, Qnil, PT, PT_BYTE);
   unbind_to (count, Qnil);
 
   return Qnil;
@@ -2902,6 +3122,8 @@ redraws with point in the center of the current window.")
   register struct window *w = XWINDOW (selected_window);
   register int ht = window_internal_height (w);
   struct position pos;
+  struct buffer *buf = XBUFFER (w->buffer);
+  struct buffer *obuf = current_buffer;
 
   if (NILP (arg))
     {
@@ -2923,13 +3145,15 @@ redraws with point in the center of the current window.")
   if (XINT (arg) < 0)
     XSETINT (arg, XINT (arg) + ht);
 
+  set_buffer_internal (buf);
   pos = *vmotion (PT, - XINT (arg), w);
 
-  Fset_marker (w->start, make_number (pos.bufpos), w->buffer);
-  w->start_at_line_beg = ((pos.bufpos == BEGV
-                          || FETCH_CHAR (pos.bufpos - 1) == '\n')
+  set_marker_both (w->start, w->buffer, pos.bufpos, pos.bytepos);
+  w->start_at_line_beg = ((pos.bytepos == BEGV_BYTE
+                          || FETCH_BYTE (pos.bytepos - 1) == '\n')
                          ? Qt : Qnil);
   w->force_start = Qt;
+  set_buffer_internal (obuf);
 
   return Qnil;
 }
@@ -2962,12 +3186,12 @@ negative means relative to bottom of window.")
   if (start < BEGV || start > ZV)
     {
       Fvertical_motion (make_number (- (height / 2)), window);
-      Fset_marker (w->start, make_number (PT), w->buffer);
+      set_marker_both (w->start, w->buffer, PT, PT_BYTE);
       w->start_at_line_beg = Fbolp ();
       w->force_start = Qt;
     }
   else
-    SET_PT (start);
+    Fgoto_char (w->start);
 
   return Fvertical_motion (arg, window);
 }
@@ -2986,11 +3210,12 @@ struct save_window_data
     /* Record the values of window-min-width and window-min-height
        so that window sizes remain consistent with them.  */
     Lisp_Object min_width, min_height;
-    /* A vector, interpreted as a struct saved_window */
+    /* A vector, each of whose elements is a struct saved_window
+       for one window.  */
     Lisp_Object saved_windows;
   };
 
-/* This is saved as a Lisp_Vector */
+/* This is saved as a Lisp_Vector  */
 struct saved_window
   {
     /* these first two must agree with struct Lisp_Vector in lisp.h */
@@ -3010,7 +3235,7 @@ struct saved_window
   ((struct saved_window *) (XVECTOR ((swv)->contents[(n)])))
 
 DEFUN ("window-configuration-p", Fwindow_configuration_p, Swindow_configuration_p, 1, 1, 0,
-  "T if OBJECT is a window-configuration object.")
+  "Return t if OBJECT is a window-configuration object.")
   (object)
      Lisp_Object object;
 {
@@ -3019,7 +3244,6 @@ DEFUN ("window-configuration-p", Fwindow_configuration_p, Swindow_configuration_
   return Qnil;
 }
 
-
 DEFUN ("set-window-configuration", Fset_window_configuration,
   Sset_window_configuration, 1, 1, 0,
   "Set the configuration of windows and buffers as specified by CONFIGURATION.\n\
@@ -3033,6 +3257,7 @@ by `current-window-configuration' (which see).")
   Lisp_Object new_current_buffer;
   Lisp_Object frame;
   FRAME_PTR f;
+  int old_point = -1;
 
   while (!WINDOW_CONFIGURATIONP (configuration))
     {
@@ -3046,6 +3271,12 @@ by `current-window-configuration' (which see).")
   new_current_buffer = data->current_buffer;
   if (NILP (XBUFFER (new_current_buffer)->name))
     new_current_buffer = Qnil;
+  else
+    {
+      if (XBUFFER (new_current_buffer) == current_buffer)
+       old_point = PT;
+
+    }
 
   frame = XWINDOW (SAVED_WINDOW_N (saved_windows, 0)->window)->frame;
   f = XFRAME (frame);
@@ -3066,6 +3297,10 @@ by `current-window-configuration' (which see).")
       int previous_frame_width =  FRAME_WIDTH  (f);
       int previous_frame_menu_bar_lines = FRAME_MENU_BAR_LINES (f);
 
+      /* The mouse highlighting code could get screwed up
+        if it runs during this.  */
+      BLOCK_INPUT;
+
       if (XFASTINT (data->frame_height) != previous_frame_height
          || XFASTINT (data->frame_width) != previous_frame_width)
        change_frame_size (f, data->frame_height, data->frame_width, 0, 0);
@@ -3075,6 +3310,15 @@ by `current-window-configuration' (which see).")
        x_set_menu_bar_lines (f, data->frame_menu_bar_lines, 0);
 #endif
 
+      if (! NILP (XWINDOW (selected_window)->buffer))
+       {
+         w = XWINDOW (selected_window);
+         set_marker_both (w->pointm,
+                          w->buffer,
+                          BUF_PT (XBUFFER (w->buffer)),
+                          BUF_PT_BYTE (XBUFFER (w->buffer)));
+       }
+
       windows_or_buffers_changed++;
       FRAME_WINDOW_SIZES_CHANGED (f) = 1;
 
@@ -3151,14 +3395,10 @@ by `current-window-configuration' (which see).")
                {
                  w->buffer = p->buffer;
                  w->start_at_line_beg = p->start_at_line_beg;
-                 set_marker_restricted (w->start,
-                                        Fmarker_position (p->start),
-                                        w->buffer);
-                 set_marker_restricted (w->pointm,
-                                        Fmarker_position (p->pointm),
-                                        w->buffer);
+                 set_marker_restricted (w->start, p->start, w->buffer);
+                 set_marker_restricted (w->pointm, p->pointm, w->buffer);
                  Fset_marker (XBUFFER (w->buffer)->mark,
-                              Fmarker_position (p->mark), w->buffer);
+                              p->mark, w->buffer);
 
                  /* As documented in Fcurrent_window_configuration, don't
                     save the location of point in the buffer which was current
@@ -3186,10 +3426,9 @@ by `current-window-configuration' (which see).")
                    set_marker_restricted (w->start, make_number (0),
                                           w->buffer);
                  if (XMARKER (w->pointm)->buffer == 0)
-                   set_marker_restricted (w->pointm,
-                                          (make_number
-                                           (BUF_PT (XBUFFER (w->buffer)))),
-                                          w->buffer);
+                   set_marker_restricted_both (w->pointm, w->buffer,
+                                               BUF_PT (XBUFFER (w->buffer)),
+                                               BUF_PT_BYTE (XBUFFER (w->buffer)));
                  w->start_at_line_beg = Qt;
                }
            }
@@ -3197,6 +3436,8 @@ by `current-window-configuration' (which see).")
 
       FRAME_ROOT_WINDOW (f) = data->root_window;
       Fselect_window (data->current_window);
+      XBUFFER (XWINDOW (selected_window)->buffer)->last_selected_window
+       = selected_window;
 
       if (NILP (data->focus_frame)
          || (FRAMEP (data->focus_frame)
@@ -3219,25 +3460,40 @@ by `current-window-configuration' (which see).")
       if (previous_frame_menu_bar_lines != FRAME_MENU_BAR_LINES (f))
        x_set_menu_bar_lines (f, previous_frame_menu_bar_lines, 0);
 #endif
+
+      UNBLOCK_INPUT;
+
+      /* Fselect_window will have made f the selected frame, so we
+        reselect the proper frame here.  Fhandle_switch_frame will change the
+        selected window too, but that doesn't make the call to
+        Fselect_window above totally superfluous; it still sets f's
+        selected window.  */
+      if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
+       do_switch_frame (data->selected_frame, Qnil, 0);
+
+      if (! NILP (Vwindow_configuration_change_hook)
+         && ! NILP (Vrun_hooks))
+       call1 (Vrun_hooks, Qwindow_configuration_change_hook);
+    }
+
+  if (!NILP (new_current_buffer))
+    {
+      Fset_buffer (new_current_buffer);
+
+      /* If the buffer that is current now is the same
+        that was current before setting the window configuration,
+        don't alter its PT.  */
+      if (old_point >= 0)
+       SET_PT (old_point);
     }
 
   /* Restore the minimum heights recorded in the configuration.  */
   window_min_height = XINT (data->min_height);
   window_min_width = XINT (data->min_width);
 
-  /* Fselect_window will have made f the selected frame, so we
-     reselect the proper frame here.  Fhandle_switch_frame will change the
-     selected window too, but that doesn't make the call to
-     Fselect_window above totally superfluous; it still sets f's
-     selected window.  */
-  if (FRAME_LIVE_P (XFRAME (data->selected_frame)))
-    do_switch_frame (data->selected_frame, Qnil, 0);
-
-  if (!NILP (new_current_buffer))
-    Fset_buffer (new_current_buffer);
-
   Vminibuf_scroll_window = data->minibuf_scroll_window;
-  return (Qnil);
+
+  return Qnil;
 }
 
 /* Mark all windows now on frame as deleted
@@ -3314,8 +3570,9 @@ save_window_save (window, vector, i)
          if (EQ (window, selected_window))
            {
              p->pointm = Fmake_marker ();
-             Fset_marker (p->pointm, BUF_PT (XBUFFER (w->buffer)),
-                          w->buffer);
+             set_marker_both (p->pointm, w->buffer,
+                              BUF_PT (XBUFFER (w->buffer)),
+                              BUF_PT_BYTE (XBUFFER (w->buffer)));
            }
          else
            p->pointm = Fcopy_marker (w->pointm, Qnil);
@@ -3430,6 +3687,118 @@ Does not restore the value of point in current buffer.")
   return unbind_to (count, val);
 }
 \f
+/* Return 1 if window configurations C1 and C2
+   describe the same state of affairs.  This is used by Fequal.   */
+
+int
+compare_window_configurations (c1, c2, ignore_positions)
+     Lisp_Object c1, c2;
+     int ignore_positions;
+{
+  register struct save_window_data *d1, *d2;
+  struct Lisp_Vector *sw1, *sw2;
+  int i;
+
+  d1 = (struct save_window_data *) XVECTOR (c1);
+  d2 = (struct save_window_data *) XVECTOR (c2);
+  sw1 = XVECTOR (d1->saved_windows);
+  sw2 = XVECTOR (d2->saved_windows);
+
+  if (! EQ (d1->frame_width, d2->frame_width))
+    return 0;
+  if (! EQ (d1->frame_height, d2->frame_height))
+    return 0;
+  if (! EQ (d1->frame_menu_bar_lines, d2->frame_menu_bar_lines))
+    return 0;
+  if (! EQ (d1->selected_frame, d2->selected_frame))
+    return 0;
+  /* Don't compare the current_window field directly.
+     Instead see w1_is_current and w2_is_current, below.  */
+  if (! EQ (d1->current_buffer, d2->current_buffer))
+    return 0;
+  if (! ignore_positions)
+    if (! EQ (d1->minibuf_scroll_window, d2->minibuf_scroll_window))
+      return 0;
+  /* Don't compare the root_window field.
+     We don't require the two configurations
+     to use the same window object,
+     and the two root windows must be equivalent
+     if everything else compares equal.  */
+  if (! EQ (d1->focus_frame, d2->focus_frame))
+    return 0;
+  if (! EQ (d1->min_width, d2->min_width))
+    return 0;
+  if (! EQ (d1->min_height, d2->min_height))
+    return 0;
+
+  /* Verify that the two confis have the same number of windows.  */
+  if (sw1->size != sw2->size)
+    return 0;
+
+  for (i = 0; i < sw1->size; i++)
+    {
+      struct saved_window *p1, *p2;
+      int w1_is_current, w2_is_current;
+
+      p1 = SAVED_WINDOW_N (sw1, i);
+      p2 = SAVED_WINDOW_N (sw2, i);
+
+      /* Verify that the current windows in the two
+        configurations correspond to each other.  */
+      w1_is_current = EQ (d1->current_window, p1->window);
+      w2_is_current = EQ (d2->current_window, p2->window);
+
+      if (w1_is_current != w2_is_current)
+       return 0;
+
+      /* Verify that the corresponding windows do match.  */
+      if (! EQ (p1->buffer, p2->buffer))
+       return 0;
+      if (! EQ (p1->left, p2->left))
+       return 0;
+      if (! EQ (p1->top, p2->top))
+       return 0;
+      if (! EQ (p1->width, p2->width))
+       return 0;
+      if (! EQ (p1->height, p2->height))
+       return 0;
+      if (! EQ (p1->display_table, p2->display_table))
+       return 0;
+      if (! EQ (p1->parent, p2->parent))
+       return 0;
+      if (! EQ (p1->prev, p2->prev))
+       return 0;
+      if (! ignore_positions)
+       {
+         if (! EQ (p1->hscroll, p2->hscroll))
+           return 0;
+         if (! EQ (p1->start_at_line_beg, p2->start_at_line_beg))
+           return 0;
+         if (NILP (Fequal (p1->start, p2->start)))
+           return 0;
+         if (NILP (Fequal (p1->pointm, p2->pointm)))
+           return 0;
+         if (NILP (Fequal (p1->mark, p2->mark)))
+           return 0;
+       }
+    }
+
+  return 1;
+}
+
+DEFUN ("compare-window-configurations", Fcompare_window_configurations,
+       Scompare_window_configurations, 2, 2, 0,
+  "Compare two window configurations as regards the structure of windows.\n\
+This function ignores details such as the values of point and mark\n\
+and scrolling positions.")
+  (x, y)
+     Lisp_Object x, y;
+{
+  if (compare_window_configurations (x, y, 1))
+    return Qt;
+  return Qnil;
+}
+\f
 init_window_once ()
 {
   selected_frame = make_terminal_frame ();
@@ -3443,6 +3812,10 @@ init_window_once ()
 
 syms_of_window ()
 {
+  staticpro (&Qwindow_configuration_change_hook);
+  Qwindow_configuration_change_hook
+    = intern ("window-configuration-change-hook");
+
   Qwindowp = intern ("windowp");
   staticpro (&Qwindowp);
 
@@ -3499,7 +3872,12 @@ There are two ways to use a list as an element:\n\
 In the first case, FRAME-PARAMETERS are used to create the frame.\n\
 In the latter case, FUNCTION is called with BUFFER as the first argument,\n\
 followed by OTHER-ARGS--it can display BUFFER in any way it likes.\n\
-All this is done by the function found in `special-display-function'.");
+All this is done by the function found in `special-display-function'.\n\
+\n\
+If this variable appears \"not to work\", because you add a name to it\n\
+but that buffer still appears in the selected window, look at the\n\
+values of `same-window-buffer-names' and `same-window-regexps'.\n\
+Those variables take precedence over this one.");
   Vspecial_display_buffer_names = Qnil;
 
   DEFVAR_LISP ("special-display-regexps", &Vspecial_display_regexps,
@@ -3514,7 +3892,12 @@ There are two ways to use a list as an element:\n\
 In the first case, FRAME-PARAMETERS are used to create the frame.\n\
 In the latter case, FUNCTION is called with the buffer as first argument,\n\
 followed by OTHER-ARGS--it can display the buffer in any way it likes.\n\
-All this is done by the function found in `special-display-function'.");
+All this is done by the function found in `special-display-function'.\n\
+\n\
+If this variable appears \"not to work\", because you add a regexp to it\n\
+but the matching buffers still appear in the selected window, look at the\n\
+values of `same-window-buffer-names' and `same-window-regexps'.\n\
+Those variables take precedence over this one.");
   Vspecial_display_regexps = Qnil;
 
   DEFVAR_LISP ("special-display-function", &Vspecial_display_function,
@@ -3577,6 +3960,17 @@ If there is only one window, it is split regardless of this value.");
     "*Delete any window less than this wide.");
   window_min_width = 10;
 
+  DEFVAR_BOOL ("scroll-preserve-screen-position",
+              &scroll_preserve_screen_position,
+    "*Nonzero means scroll commands move point to keep its screen line unchanged.");
+  scroll_preserve_screen_position = 0;
+
+  DEFVAR_LISP ("window-configuration-change-hook",
+              &Vwindow_configuration_change_hook,
+    "Functions to call when window configuration changes.\n\
+The selected frame is the one whose configuration has changed.");
+  Vwindow_configuration_change_hook = Qnil;
+
   defsubr (&Sselected_window);
   defsubr (&Sminibuffer_window);
   defsubr (&Swindow_minibuffer_p);
@@ -3632,6 +4026,7 @@ If there is only one window, it is split regardless of this value.");
   defsubr (&Sset_window_configuration);
   defsubr (&Scurrent_window_configuration);
   defsubr (&Ssave_window_excursion);
+  defsubr (&Scompare_window_configurations);
 }
 
 keys_of_window ()