diff --git a/src/window.c b/src/window.c
index 4aeb8b3..8fdc9f9 100644
--- a/src/window.c
+++ b/src/window.c
@@ -3228,6 +3228,16 @@ If WINDOW is omitted or nil, it defaults to the selected window.  */)
   return Qnil;
 }
 
+DEFUN ("force-wsf", Fforce_wsf, Sforce_wsf, 0, 1, 0,
+       doc: /* Set wsf_toggle so that redisplay_window will run `wsf-hook'
+(at the appropriate time) even if no window scrolling occurs.  */)
+  (Lisp_Object window)
+{
+  register struct window *w = decode_live_window (window);
+    w->wsf_toggle = true;
+    return window;
+}
+
 /* Make WINDOW display BUFFER.  RUN_HOOKS_P means it's allowed
    to run hooks.  See make_frame for a case where it's not allowed.
    KEEP_MARGINS_P means that the current margins, fringes, and
@@ -7421,6 +7431,7 @@ displayed after a scrolling operation to be somewhat inaccurate.  */);
   defsubr (&Sset_window_buffer);
   defsubr (&Srun_window_configuration_change_hook);
   defsubr (&Srun_window_scroll_functions);
+  defsubr (&Sforce_wsf);
   defsubr (&Sselect_window);
   defsubr (&Sforce_window_update);
   defsubr (&Ssplit_window_internal);
diff --git a/src/window.h b/src/window.h
index c29207d..11aea13 100644
--- a/src/window.h
+++ b/src/window.h
@@ -348,6 +348,9 @@ struct window
        Vwindow_scroll_functions; also by Frecenter with argument.  */
     bool_bf optional_new_start : 1;
 
+    /* True means force a call to Vwsf_hook.  */
+    bool_bf wsf_toggle : 1;
+
     /* True means the cursor is currently displayed.  This can be
        set to zero by functions overpainting the cursor image.  */
     bool_bf phys_cursor_on_p : 1;
diff --git a/src/xdisp.c b/src/xdisp.c
index d730a0b..416f632 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -13672,6 +13672,7 @@ redisplay_internal (void)
       && match_p
       && !w->force_start
       && !w->optional_new_start
+      && !w->wsf_toggle
       /* Point must be on the line that we have info recorded about.  */
       && PT >= CHARPOS (tlbufpos)
       && PT <= Z - CHARPOS (tlendpos)
@@ -15030,6 +15031,22 @@ run_window_scroll_functions (Lisp_Object window, struct text_pos startp)
   return startp;
 }
 
+static struct text_pos
+run_wsf (Lisp_Object window, struct text_pos startp, struct text_pos endp)
+{
+  struct window *w = XWINDOW (window);
+  SET_MARKER_FROM_TEXT_POS (w->start, startp);
+  eassert (current_buffer == XBUFFER (w->contents));
+  if (!NILP (Vwsf_hook)
+      && (Flocal_variable_p (Qwsf_var, Fwindow_buffer (window))))
+    {
+      CALLN (Frun_hook_with_args, Qwsf_hook, window, make_number (CHARPOS (startp)), make_number (CHARPOS (endp)));
+      SET_TEXT_POS_FROM_MARKER (startp, w->start);
+      /* In case the hook functions switch buffers.  */
+      set_buffer_internal (XBUFFER (w->contents));
+    }
+  return startp;
+}
 
 /* Make sure the line containing the cursor is fully visible.
    A value of true means there is nothing to be done.
@@ -15371,6 +15388,11 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   /* Run window scroll functions.  */
   startp = run_window_scroll_functions (window, startp);
 
+  startp = run_wsf (window, startp, startp);
+#ifdef GLYPH_DEBUG
+  debug_method_add (w, "run_wsf [1 of 3] was executed");
+#endif
+
   /* Display the window.  Give up if new fonts are loaded, or if point
      doesn't appear.  */
   if (!try_window (window, startp, 0))
@@ -16225,13 +16247,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
   /* If someone specified a new starting point but did not insist,
      check whether it can be used.  */
-  if ((w->optional_new_start || window_frozen_p (w))
+  if ((w->optional_new_start || w->wsf_toggle || window_frozen_p (w))
       && CHARPOS (startp) >= BEGV
       && CHARPOS (startp) <= ZV)
     {
       ptrdiff_t it_charpos;
 
-      w->optional_new_start = false;
       start_display (&it, w, startp);
       move_it_to (&it, PT, 0, it.last_visible_y, -1,
 		  MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
@@ -16252,14 +16273,28 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	    w->force_start = true;
 #ifdef GLYPH_DEBUG
 	  if (w->force_start)
-	    {
-	      if (window_frozen_p (w))
-		debug_method_add (w, "set force_start from frozen window start");
-	      else
-		debug_method_add (w, "set force_start from optional_new_start");
-	    }
+    {
+      if (window_frozen_p (w))
+        {
+        debug_method_add (w, "set force_start from frozen window start");
+      }
+      else if (w->optional_new_start)
+        {
+        debug_method_add (w, "set force_start from optional_new_start");
+      }
+      else if (w->wsf_toggle)
+        {
+        debug_method_add (w, "set force_start from wsf_toggle");
+      }
+      else
+        {
+        debug_method_add (w, "This situation is not yet contemplated.");
+      }
+    }
 #endif
 	}
+      w->optional_new_start = false;
+      w->wsf_toggle = false;
     }
 
  force_start:
@@ -16287,11 +16322,19 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	 than to get into an infinite loop calling the hook functions
 	 and having them get more errors.  */
       if (!update_mode_line
-	  || ! NILP (Vwindow_scroll_functions))
+	  || ! NILP (Vwindow_scroll_functions)
+	  || ! NILP (Vwsf_hook))
 	{
 	  update_mode_line = true;
 	  w->update_mode_line = true;
+  /* Run window scroll functions.  */
 	  startp = run_window_scroll_functions (window, startp);
+
+  startp = run_wsf (window, startp, startp);
+#ifdef GLYPH_DEBUG
+  debug_method_add (w, "run_wsf [2 of 3] was executed");
+#endif
+
 	}
 
       if (CHARPOS (startp) < BEGV)
@@ -16559,6 +16602,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	     because a window scroll function can have changed the
 	     buffer.  */
 	  || !NILP (Vwindow_scroll_functions)
+	  || !NILP (Vwsf_hook)
 	  || MINI_WINDOW_P (w)
 	  || !(used_current_matrix_p
 	       = try_window_reusing_current_matrix (w)))
@@ -16756,6 +16800,11 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   /* Run scroll hooks.  */
   startp = run_window_scroll_functions (window, it.current.pos);
 
+  startp = run_wsf (window, it.current.pos, it.current.pos);
+#ifdef GLYPH_DEBUG
+  debug_method_add (w, "run_wsf [3 of 3] executed");
+#endif
+
   /* Redisplay the window.  */
   bool use_desired_matrix = false;
   if (!current_matrix_up_to_date_p
@@ -16764,6 +16813,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       /* Don't use try_window_reusing_current_matrix in this case
 	 because it can have changed the buffer.  */
       || !NILP (Vwindow_scroll_functions)
+      || !NILP (Vwsf_hook)
       || !just_this_one_p
       || MINI_WINDOW_P (w)
       || !(used_current_matrix_p
@@ -23616,6 +23666,20 @@ decode_mode_spec (struct window *w, register int c, int field_width,
 	  return "@";
       }
 
+    case 'w':
+      {
+  ptrdiff_t window_start = marker_position (w->start);
+  pint2str (decode_mode_spec_buf, width, window_start);
+  return decode_mode_spec_buf;
+      }
+
+    case 'W':
+      {
+  ptrdiff_t window_end = BUF_Z (b) - w->window_end_pos;
+  pint2str (decode_mode_spec_buf, width, window_end);
+  return decode_mode_spec_buf;
+      }
+
     case 'z':
       /* coding-system (not including end-of-line format) */
     case 'Z':
@@ -30928,6 +30992,7 @@ They are still logged to the *Messages* buffer.  */);
   DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");
   DEFSYM (Qoverriding_local_map, "overriding-local-map");
   DEFSYM (Qwindow_scroll_functions, "window-scroll-functions");
+  DEFSYM (Qwsf_hook, "wsf-hook");
   DEFSYM (Qwindow_text_change_functions, "window-text-change-functions");
   DEFSYM (Qredisplay_end_trigger_functions, "redisplay-end-trigger-functions");
   DEFSYM (Qinhibit_point_motion_hooks, "inhibit-point-motion-hooks");
@@ -31249,6 +31314,16 @@ is scrolled.  It is not designed for that, and such use probably won't
 work.  */);
   Vwindow_scroll_functions = Qnil;
 
+  DEFVAR_LISP ("wsf-hook", Vwsf_hook,
+    doc: /* Used to obtain the new `window-start' during redisplay.  */);
+  Vwsf_hook = Qnil;
+
+  DEFVAR_LISP ("wsf-var", Vwsf_var,
+    doc: /* `wsf-hook` checks to see if `wsf-var` is set locally.  */);
+  Vwsf_var = Qnil;
+  Fmake_variable_buffer_local (Qwsf_var);
+  DEFSYM (Qwsf_var, "wsf-var");
+
   DEFVAR_LISP ("window-text-change-functions",
 	       Vwindow_text_change_functions,
     doc: /* Functions to call in redisplay when text in the window might change.  */);
