/*
 * Epoch 4.0 display code - generate display from window structure and
 * buffer contents.  Produce data used by display for either X11 or "ascii"
 * terminals.
 *
 * $Revision: 1.111 $  
 * $Source: /import/kaplan/stable/distrib/epoch-4.2/src/RCS/xdispepoch.c,v $
 * $Date: 92/08/26 23:14:21 $ 
 * $Author: marca $
 */
#ifndef LINT
static char rcsid[] = "$Author: marca $ $Date: 92/08/26 23:14:21 $ $Source: /import/kaplan/stable/distrib/epoch-4.2/src/RCS/xdispepoch.c,v $ $Revision: 1.111 $";
#endif

#include "config.h"
#include <stdio.h>
/*#include <ctype.h>*/
#undef NULL
#include "lisp.h"
#include "window.h"
#include "termchar.h"
#include "buffer.h"
#include "indent.h"
#include "commands.h"
#include "macros.h"
#include "termhooks.h"

#include "button.h"		/* Epoch */
#include "dispepoch.h"		/* Epoch */
#include "screen.h"		/* Epoch */
#include "screenW.h"		/* Epoch */

#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define abs(a) ((a) < 0 ? (-(a)) : (a))

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Declarations:
 */
/* Pointers to (free) lists for char_block, line_header, and font
 * structures.
 */
struct char_block *block_free;
struct line_header *line_free;

/* Globals used during redisplay process to locate cursor at point */
static int point_vpos;
static int point_hpos;
static struct line_header *point_v;
static struct char_block *point_h;

/* Nonzero means print newline before next minibuffer message.  */

int noninteractive_need_newline;

/* Style for use when laying out mode line.
 */
struct Lisp_Style *mode_style;

struct position *layout_text_line();
char *decode_mode_spec ();

/* Format for global mode lines (unless set buffer-local) */
Lisp_Object Vglobal_mode_string;

/* Marker for where to display an arrow on top of the buffer text. */
Lisp_Object Voverlay_arrow_position;

/* String to display for the arrow. */
Lisp_Object Voverlay_arrow_string;

/* Values of those variables at last redisplay.  */
Lisp_Object last_arrow_position, last_arrow_string;

/* Next button in button list for a given window in redisplay */
Lisp_Object next_button;

/* If cursor motion alone moves point off screen,
   Try scrolling this many lines up or down if that will bring it back.  */
int scroll_step;

/* Nonzero means send various TERMCAP strings when screen is cleared.  */
int reset_terminal_on_clear;

/* Nonzero means truncate lines in all windows less wide than the screen */
int truncate_partial_width_windows;

/* Number of windows showing the buffer of the selected window.
   keyboard.c refers to this.  */
int buffer_shared;

int windows_or_buffers_changed;

int mode_line_inverse_video;

/* Value of echo_area_contents when it was last acted on.
  If this is nonzero, there is a message on the screen
  in the minibuffer and it should be erased as soon
  as it is no longer requested to appear. */
char *prev_echo_area_contents;

/* Prompt to display in front of the minibuffer contents */
char *minibuf_prompt;

/* Width in columns of current minibuffer prompt.  */
int minibuf_prompt_width;

int update_mode_lines;

/* Smallest number of characters before the gap
   at any time since last redisplay that finished.
   Valid for current buffer when try_window_id can be called.  */
int beg_unchanged;

/* Smallest number of characters after the gap
   at any time since last redisplay that finished.
   Valid for current buffer when try_window_id can be called.  */
int end_unchanged;

/* MODIFF as of last redisplay that finished;
 if it matches MODIFF, beg_unchanged and end_unchanged
 contain no useful information */
int unchanged_modified;

/* Message to display instead of minibuffer contents
   This is what the functions error and message make,
   and command echoing uses it as well.
   It overrides the minibuf_prompt as well as the buffer.  */
char *echo_area_contents;

int clip_changed;

int blank_end_of_window;

/* The buffer position of the first character appearing
 entirely or partially on the current screen line.
 Or zero, which disables the optimization for the current screen line. */
int this_line_bufpos;

/* Number of characters past the end of this line,
   including the terminating newline */
static int this_line_endpos;

/* The vertical position of this screen line. */
static int this_line_vpos;

static struct line_header *this_line_line;

/* Hpos value for start of display on this screen line.
   Usually zero, but negative if first character really began
   on previous line */
static int this_line_start_hpos;

/* Buffer that this_line variables are describing. */
static struct buffer *this_line_buffer;

struct char_block *layout_string_inc();

extern Lisp_Object 	Vx_global_update;
extern Lisp_Object	Vsynchronize_minibuffers;
extern int 		distinct_minibuffer;
extern int 		screen_garbaged;
extern int 		interrupt_input;
extern int 		command_loop_level;
extern int		display_completed;

extern struct char_block old_cur_char;
extern struct line_header old_cur_line;

extern struct buffer *clip_buffer;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Pending redisplay blocks for a given window.  Stack should be empty at
 * beginning and end of redisplay for window.
 */
struct redisplay_block display_list[300];
int list_ptr; 

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Allocate a pool of line header blocks
 */
int
line_pool()
{
  struct line_header *t;
  int i;

  t = (struct line_header *) malloc(100*sizeof(struct line_header));
  if (!t) return 1;
  bzero(t,100*sizeof(struct line_header));

  /* Now, add these new blocks to the start of the free list
   * don't worry about prev pointers.
   */
  for (i = 0; i < 100; i++)
    {
      t->next = line_free;
      line_free = t;
      t++;
    }
  return 0;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return a new line header block
 */
struct line_header *
get_line()
{
  struct line_header *l;
  
  if (!line_free && line_pool())
    fprintf(stderr,"Couldn't allocate space"), abort();

  l = line_free;
  line_free = line_free->next;
  l->new = 1;
  l->tabs = 0;
  l->next = l->prev = 0;
  l->body = get_block();
  l->end = l->body;
  l->body->ch = 0;
  l->body->new = 0;

  return l;  
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Either return the next line_header for this window, or return a new
 * one.
 */
struct line_header *
next_line(l)
     struct line_header *l;
{
  struct line_header *n;
  if (!l->next)
    {
      n = get_line();
      l->next = n;
      n->prev = l;
      return n;
    }
  return l->next;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Allocate a pool of character block structures
 */
int
block_pool()
{
  struct char_block *t;
  int i;

  t = (struct char_block *) malloc(1000*sizeof(struct char_block));
  if (!t) return 1;

  bzero(t,1000*sizeof(struct char_block));
  
  /* Now, add these new blocks to the start of the free list
   * don't worry about prev pointers.
   */
  for (i = 0; i < 1000; i++)
    {
      t->next = block_free;
      block_free = t;
      t++;
    }
  return 0;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return/allocate a char_block structure
 */
struct char_block *
get_block()
{
  struct char_block *f;
  
  if (!block_free && block_pool())
    fprintf(stderr,"Couldn't allocate space"), abort();

  f = block_free;
  block_free = block_free->next;
  bzero(f,sizeof (struct char_block));
  f->new = 1;
  return f;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Either return the next char_block on this line (not sentinel), or 
 * a new one.
 */
struct char_block *
next_block(c,l)
     struct char_block *c;
     struct line_header *l;
{
  struct char_block *d;
  
  if (!c->next || c->ch == 0)
    {
      /* Insert new block prior to end of line dummy */
      d = get_block();
      if (l->body == c) l->body = d;
      if (c->prev) c->prev->next = d;
      d->prev = c->prev;
      d->next = c;
      c->prev = d;
      return d;
    }
  /* node is already next in list, so return it */
  return c;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return a bunch of char_block structures to the free list
 */
void
free_blocks(b,e)
     struct char_block *b,*e;
{
  e->next = block_free;
  block_free = b;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return a line of char_block structures to the free list and free the
 * line_header structure itself.
 */
void
free_line(l)
     struct line_header *l;
{
  struct char_block *b;

  b = l->end;
  if (b)
    {
      b->next = block_free;
      block_free = l->body;
    }
  bzero(l,sizeof(struct line_header));
  l->next = line_free;
  line_free = l;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void
free_lines(l)
     struct line_header *l;
{
  struct line_header *p;
  struct char_block *b;

  p = l;
  while (l)
    {
      l = l->next;
      free_line(p);
      p = l;
    }
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("epoch::set-screen-modified",Fset_screen_modified,Sset_screen_modified,0,1,0,
       "Inform Epoch that given (or current) screen has been modified and\n\
should be redisplayed as soon as possible.")
    (screen)
    Lisp_Object screen;
{
  extern Lisp_Object find_block();

  screen = find_block(screen);
  if (NULL(screen)) return Qnil;
  XROOT(screen)->needs_update = Qt;
  windows_or_buffers_changed++;
  screen_changed++;
  return Qnil;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("redraw-display", Fredraw_display, Sredraw_display, 0, 0, "",
  "Clear the screen and output again what is supposed to appear on it.")
  ()
{
  struct Root_Block *rb = cur_root;

  if (reset_terminal_on_clear)
    set_terminal_modes ();

  mark_window_display_accurate(rb->ewin,0);
  
  if (inhibit_window_system)
    fflush (stdout);
  
  windows_or_buffers_changed++;
  screen_changed++;

  XWSCREEN(rb->win)->replot_lines = 1;			       
  
  return Qnil;  
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("epoch::redisplay-screen",Fredisplay_screen,Sredisplay_screen,0,1,0,
       "Perform a smart redisplay of the given (or current) screen.")
     (screen)
     Lisp_Object screen;
{
  if (NULL(screen))
    screen = XWINDOW(XWINDOW(minibuf_window)->prev)->root;
  if (!ROOTP(screen))
    error("expected a screen");

  XROOT(screen)->needs_update = Qt;
  mark_window_display_accurate(XROOT(screen)->ewin,0);
  windows_or_buffers_changed++;
  screen_changed++;

  buffer_shared = 0;
  redisplay_screen(XROOT(screen),1);
  return screen;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Buffer used for messages formatted by `message', and by print  */
char message_buf[301];

/* Nonzero if message_buf is being used by print;
   zero if being used by message.  */
int message_buf_print;

/* dump an informative message to the minibuf */
/* this now uses varargs calls when compiled with GCC;
   patch due to kris@diku.dk */

#ifdef __GNUC__

#include <stdarg.h>
message (char *m,...)
{
  int a1,a2,a3; va_list ap;

#else /* if !__GNUC__ */

/* VARARGS 1 */
message (m, a1, a2, a3)
     char *m;
{

#endif /* !__GNUC__ */

  struct Root_Block *rb;

  rb = distinct_minibuffer ? mini_root : cur_root;
  
  if (noninteractive)
    {
      if (noninteractive_need_newline)
	putchar ('\n');
      noninteractive_need_newline = 0;

#ifdef __GNUC__
      va_start(ap,m); vprintf (m, ap); va_end(ap);
#else /* if !__GNUC__ */
      printf (m, a1, a2, a3);
#endif /* !__GNUC__ */

      printf ("\n");
      fflush (stdout);
    }
  else if (FROM_KBD)
    {
      extern int printbufidx;
#ifdef NO_ARG_ARRAY

      int a[3];

#ifdef __GNUC__
      va_start(ap,m);
      a[0] = va_arg(ap,int);
      a[1] = va_arg(ap,int);
      a[2] = va_arg(ap,int);
      va_end(ap);
#else /* if !__GNUC__ */
      a[0] = a1;
      a[1] = a2;
      a[2] = a3;
#endif /* !__GNUC__ */

      doprnt (message_buf, sizeof message_buf - 1, m, 3, a);

#else /* if !NO_ARG_ARRAY */

#ifdef __GNUC__
      va_start(ap,m);
      doprnt (message_buf, sizeof message_buf - 1, m, 3, ap);
      va_end(ap);
#else /* if !__GNUC__ */
      doprnt (message_buf, sizeof message_buf - 1, m, 3, &a1);
#endif /* !__GNUC__ */

#endif /* !NO_ARG_ARRAY */

      echo_area_contents = message_buf;
      message_buf_print = 0;
      printbufidx = 0;
      do {
	hold_window_change();
	layout_echo_area_contents ();
	unhold_window_change();
      } while (screen_garbaged);
    }
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Specify m, a string, as a message in the minibuf.  */
message1 (m)
     char *m;
{
  struct Root_Block *rb;

  rb = distinct_minibuffer ? mini_root : cur_root;
  
  if (noninteractive)
    {
      if (noninteractive_need_newline)
	putchar ('\n');
      noninteractive_need_newline = 0;
      printf ("%s\n", m);
      fflush (stdout);
    }
  else if (FROM_KBD)
    {
      echo_area_contents = m;
      do {
	hold_window_change();
	layout_echo_area_contents ();
	unhold_window_change();
      } while (screen_garbaged);
    }
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Layout a string for display in the minibuffer/echo area.
 */
layout_echo_area_contents()
{
  struct Root_Block *rb;
  struct W_Screen *ws;
  struct window *w;
  struct line_header *l;
  struct char_block *changed;
  int hpos = 0,lwidth;

  rb = distinct_minibuffer ? mini_root : cur_root;
  ws = XWSCREEN(rb->win);
  w = XWINDOW(rb->minibuf_window);

  l = XLINE(w->lines);
  if (!l)
    {
      l = get_line();
      XSET(w->lines,Lisp_Raw_Data,l);
      ws->cursor_x = ws->cursor_y = 0;
      ws->cur_w = ws->new_cur_w = w;
      ws->cur_char = ws->new_cur_char = l->body;
      ws->cur_line = ws->new_cur_line = l;
    }

  if (screen_garbaged)
    {
      Fredraw_display();
      screen_garbaged = 0;
    }

  if (echo_area_contents || minibuf_level == 0)
    {
      lwidth = l->lwidth;
      changed = layout_string_inc(w,echo_area_contents ? echo_area_contents
				  : "",0,0,0,ws->pixw,
				  l,0,&hpos,1);
      l->prevy = l->ypos;	/* Prev value */
      l->ypos = w->pixtop + l->ascent;
      /*
       * Make a redisplay block corresponding to this line in echo area
       */
      if (l->changed)
	{
	  list_ptr = 0;		/* First (only) entry */
	  BLOCK_TYPE(list_ptr) = LINE;
	  BLOCK_LINE(list_ptr) = l;
	  BLOCK_LINE_START(list_ptr) = changed;
	  BLOCK_LINE_END(list_ptr) = 0;
	  BLOCK_LINE_CLEAR(list_ptr) = (lwidth > l->lwidth);
	  /* Do the update */
	  update_window (w);
	}

      if (!distinct_minibuffer)
	{
	  /* Multiple minibuffers; must ensure coherency between them. */
	  rb = root;
	  do
	    {
	      if (rb != cur_root)
		{
		  ws = XWSCREEN(rb->win);
		  w = XWINDOW(rb->minibuf_window);
		  l = XLINE(w->lines);
		  if (!l)
		    {
		      l = get_line();
		      XSET(w->lines,Lisp_Raw_Data,l);
		      ws->cursor_x = ws->cursor_y = 0;
		      ws->new_cur_w = ws->new_cur_w = w;
		      ws->cur_char = ws->new_cur_char = l->body;
		      ws->cur_line = ws->new_cur_line = l;
		    }
		  if (NULL(Vsynchronize_minibuffers))
		    {
		      if (l->body != l->end)
			{
			  free_blocks (l->body,l->end->prev);
			  l->end->xpos = w->pixleft;
			  l->body = l->end;
			  l->chars = 0;
			  l->changed = 1;
			  changed = l->body;
			}
		    }
		  else
		    {
		      lwidth = l->lwidth;
		      changed = layout_string_inc(w,echo_area_contents ?
						  echo_area_contents : "",
						  0,0,0,ws->pixw,
						  l,0,
						  &hpos,1);
		    }
		  l->prevy = l->ypos; /* Prev value */
		  l->ypos = w->pixtop + l->ascent;
		  /*
		   * Make a redisplay block corresponding to this line in echo
		   * area
		   */
		  if (l->changed)
		    {
		      list_ptr = 0;	/* First (only) entry */
		      BLOCK_TYPE(list_ptr) = LINE;
		      BLOCK_LINE(list_ptr) = l;
		      BLOCK_LINE_START(list_ptr) = changed;
		      BLOCK_LINE_END(list_ptr) = 0;
		      BLOCK_LINE_CLEAR(list_ptr) = (lwidth > l->lwidth);
		      /* Do the update */
		      update_window (w);
		    }
		}
	      rb = XROOT(rb->next);
	    }
	  while (rb != root);
	}
    }
  /*
   * If we are in distinct minibuffer screen, minibuffer text is always
   * on line 1.  Otherwise, its on the last line of the screen.
   */
  if ((!distinct_minibuffer && ws->cursor_y == ws->height)
      || (distinct_minibuffer && ws->cursor_y == 1))
    {
      ws->cursor_x = hpos;
      ws->cur_w = ws->new_cur_w = XWINDOW(rb->minibuf_window);
      ws->cur_char = ws->new_cur_char = l->end;
      ws->cur_line = ws->new_cur_line = l;
    }

  if (EQ(minibuf_window,selected_window))
    this_line_bufpos = 0;

  prev_echo_area_contents = echo_area_contents;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Synchronize contents of minibuffer for 
 */
sync_minibuffer(window)
     Lisp_Object window;
{
  register struct window *w = XWINDOW(window);
  register struct window *m = XWINDOW(minibuf_window);

  w->buffer = m->buffer;
  w->start = m->start;
  w->last_modified = 0; /* m->last_modified; */
  w->hscroll = m->hscroll;
  w->pointm = m->pointm;
  w->force_start = m->force_start;
  w->last_point = m->last_point;
  w->last_point_x = m->last_point_x;
  w->last_point_y = m->last_point_y;
  w->last_mark_x = m->last_mark_x;
  w->last_mark_y = m->last_mark_y;
  w->window_end_pos = m->window_end_pos;
  w->window_end_valid = m->window_end_valid;
  w->window_end_ppos = m->window_end_ppos;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Perform a redisplay of all screens.  This function and
 * redisplay_preserve_echo_area() are main entry points to redisplay
 * procedure.
 */
void
redisplay()
{
  struct window *w = XWINDOW(selected_window);  
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  int update_all = !NULL(Vx_global_update) && !EQ(Qt,Vx_global_update);
  register int pause = 0;
  int all_windows;
  struct line_header *l;
  register int tlbufpos, tlendpos;

  if (noninteractive) return;

  hold_window_change();  
  rb = root;
  if (screen_garbaged)
    {
      Fredraw_display();
      screen_garbaged = 0;
    }

  if (echo_area_contents != 0 || prev_echo_area_contents != 0)
    {
      layout_echo_area_contents();
    }

  if (clip_changed || windows_or_buffers_changed || screen_changed)
    update_mode_lines++;

  if (XFASTINT (w->last_modified) < MODIFF
      && XFASTINT (w->last_modified) <= current_buffer->save_modified)
    {
      w->update_mode_line = Qt;
      if (buffer_shared > 1)
	update_mode_lines++;
    }

  all_windows = update_mode_lines || buffer_shared > 1;

  /* If specs on arrow have changed, do thorough redisplay
   * to ensure no arrows exist at prev position.
   */
  if (Voverlay_arrow_position != last_arrow_position
      || Voverlay_arrow_string != last_arrow_string)
    all_windows = 1, clip_changed = 1;

  tlbufpos = this_line_bufpos;
  tlendpos = this_line_endpos;
  if (!all_windows && tlbufpos > 0 && NULL (w->update_mode_line)
      && this_line_buffer == current_buffer
      && current_buffer == XBUFFER(w->buffer)
      && NULL(w->force_start)
      && point >= tlbufpos
      && point <= Z - tlendpos
      && (XFASTINT(w->last_modified) >= MODIFF
	  || (beg_unchanged >= tlbufpos - 1
	      && GPT >= tlbufpos
	      && ((XTYPE(current_buffer->selective_display) == Lisp_Int
		  && XINT(current_buffer->selective_display) > 0
		  ? (beg_unchanged >= tlbufpos
		     && GPT > tlbufpos)
		  : 1))
	      && end_unchanged >= tlendpos
	      && Z - GPT >= tlendpos)))
    {
      if (tlbufpos > BEGV && FETCH_CHAR(tlbufpos - 1) != '\n'
	  && (tlbufpos == ZV
	      || FETCH_CHAR(tlbufpos) == '\n'))
	{
	  goto cancel;
	}
	  
      if (XFASTINT(w->last_modified) < MODIFF
	  || EQ(selected_window,minibuf_window))
	{
	  int fail = 0;

	  old_cur_char.ch = ws->cur_char->ch;
	  old_cur_char.xpos = ws->cur_char->xpos;
	  old_cur_char.style = ws->cur_char->style;

	  old_cur_line.chars = ws->cur_line->chars;
	  old_cur_line.ypos = ws->cur_line->ypos;
	  old_cur_line.ascent = ws->cur_line->ascent;
	  old_cur_line.descent = ws->cur_line->descent; 	  

	  point_vpos = -1;
	  list_ptr = -1;
	  next_button = w->start_button; /* starting point in button list */

	  layout_text_line(w,this_line_vpos,tlbufpos,this_line_start_hpos,
			   pos_tab_offset(w,tlbufpos),
			   &fail,this_line_line);

	  /*
	   * If propogating anything forward, fail here and redo entire
	   * window
	   */
	  if (fail)
	    {
	      struct char_block *cb = this_line_line->end->prev;	      

	      if (list_ptr == 0)
		{
		  if (BLOCK_LINE_START(0))
		    BLOCK_LINE_START(0)->ch = 0;
		  if (BLOCK_LINE_END(0))
		    BLOCK_LINE_END(0)->ch = 0;
		}
	      if (cb) cb->ch = 0;

	      ws->cur_char = &old_cur_char;
	      ws->cur_line = &old_cur_line;

	      goto cancel;
	    }

	  if (point_vpos >= 0 && this_line_bufpos
	      && this_line_endpos == tlendpos)
	    {
	      if (interrupt_input)
		unrequest_sigio();
	      stop_polling();

	      update_window(w);

	      if (interrupt_input)
		request_sigio();
	      start_polling();

	      goto finish;
	    }
	  else
	    {
	      struct char_block *cb = this_line_line->end->prev;
	      
	      /* UGLY:  Here we will plot the \ or $ at end of line, and
	       * lose incremental display capabilities when we try laying
	       * out the line again.  "Blot" out the changed characters
	       * so they will get plotted.
	       */
	      if (list_ptr == 0)
		{
		  if (BLOCK_LINE_START(0))
		    BLOCK_LINE_START(0)->ch = 0;
		  if (BLOCK_LINE_END(0))
		    BLOCK_LINE_END(0)->ch = 0;
		}
	      if (cb) cb->ch = 0;

	      ws->cur_char = &old_cur_char;
	      ws->cur_line = &old_cur_line;
	      
	      goto cancel;
	    }
	}
      else if (point == XFASTINT(w->last_point))
	{
	  if (echo_area_contents || prev_echo_area_contents) goto finish;
	  return;
	}
      else
	{
	  struct position pos;
	  
	  pos = *compute_motion(tlbufpos,0,
				XINT (w->hscroll) ? 1 - XINT(w->hscroll) : 0,
				point,2, -(1 << (SHORTBITS - 1)),
				XINT(w->hscroll),
				pos_tab_offset(w,tlbufpos),w,
				w->start_button);


	  if (pos.vpos < 1)
	    {
	      struct char_block *cb;
	      int i;
	      
	      ws->cursor_x = max (0,pos.hpos);
	      ws->cursor_y = this_line_vpos;
	      
	      ws->new_cur_line = this_line_line;

	      cb = ws->new_cur_line->body;
	      i = ws->cursor_x + (w->pixleft != ws->in_border);
	      while (cb && i--) cb = cb->next;
	      ws->new_cur_char = cb;
	      ws->new_cur_w = w;

	      if (interrupt_input)
		unrequest_sigio();
	      stop_polling();

	      update_window(w);

	      if (interrupt_input)
		request_sigio();
	      start_polling();

	      goto finish;
	    }
	  else
	    {
	      goto cancel;
	    }
	}

    }
 cancel:
  
  this_line_bufpos = 0;
      
  /*
   * If in a distinct screen, redisplay minibuffer everytime we haven't
   * done so above.
   */
  if (distinct_minibuffer && echo_area_contents == 0 && prev_echo_area_contents == 0)
    {
      /* Note:  bang out minibuffer contents regardless */
      redisplay_screen(mini_root,1);
    }

  rb = XROOT(EDIT_SCREEN);
  if (all_windows)
    {
      /* Do all windows.
       * Update current edit screen (if not already done
       * by updating minibuffer)
       */
      buffer_shared = 0;
      if (rb != mini_root)
	{
	  redisplay_screen(rb,1);
	}
      for (rb = XROOT(cur_root->next); rb != cur_root && !pause ;
	   rb = XROOT(rb->next))
	{
	  if ((rb != XROOT(EDIT_SCREEN)) && (rb != mini_root) &&
	      (update_all ||
	       (EQ(Qt,Vx_global_update) && EQ(Qt,rb->update) &&
		EQ(Qt,rb->needs_update))))
	    {
	      pause |= redisplay_screen(rb,0);
	    }
	}
    }
  else
    {
      /* Only do selected window */
      if (rb != mini_root)
	{
	  layout_window(selected_window,1);
	}
    }

 finish:
  if (!pause)
    {
      struct buffer *b = XBUFFER(w->buffer);
      current_buffer->force_redisplay = Qnil;
      clip_changed = 0;
      clip_buffer = 0;
      blank_end_of_window = 0;
      unchanged_modified = BUF_MODIFF (b);
      beg_unchanged = BUF_GPT (b) - BUF_BEG (b);
      end_unchanged = BUF_Z (b) - BUF_GPT (b);

      XFASTINT(w->last_point) = BUF_PT (b);
      XFASTINT(w->last_point_x) = ws->cursor_x;
      XFASTINT(w->last_point_y) = ws->cursor_y;
      if (all_windows)
	{
	  mark_all_windows_display_accurate(1);
	}
      else
        {
	  w->update_mode_line = Qnil;
	  XFASTINT(w->last_modified) = BUF_MODIFF(b);
	  w->window_end_valid = Qt;
	  last_arrow_position = Voverlay_arrow_position;
	  last_arrow_string = Voverlay_arrow_string;
	}
      update_mode_lines = 0;
      windows_or_buffers_changed = screen_changed = 0;
    }
  else
    {
      /* Ugly:  we got preempted doing extra screens.  buffer_shared value is
       * invalid; increment it so that we'll try to get through all windows
       * again next time.
       */
      this_line_bufpos = 0;
      buffer_shared++;
    }

  if (screen_garbaged)
    redisplay ();
  
  unhold_window_change();

}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
redisplay_preserve_echo_area ()
{
  if (echo_area_contents == 0 && prev_echo_area_contents != 0)
    {
      echo_area_contents = prev_echo_area_contents;
      redisplay ();
      echo_area_contents = 0;
    }
  else
    redisplay ();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* FORCE nonzero means do not stop for pending input
 */
int
redisplay_screen(rb,force)
     struct Root_Block *rb;
     int force;
{
  extern int input_pending;

  detect_input_pending();
  if ((input_pending || XWSCREEN(rb->win)->size_change) && !force)
    {
      display_completed = 0;
      return 1;
    }
  
  RW_FIXUP(rb);
  layout_windows(rb->ewin);

  if (termscript)
    fflush(termscript);
  if (inhibit_window_system)
    fflush(stdout);

  mark_window_display_accurate(rb->ewin,1);
  XWSCREEN(rb->win)->replot_lines = 0;
  return 0;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
clear_display_structs(window)
     Lisp_Object window;
{
  register struct window *w;
  struct Root_Block *rb;
  struct W_Screen *ws;
  struct line_header *l,*p;

  /* This is called from delete-window
   */
  w = XWINDOW(window);
  ws = XWSCREEN(XROOT(w->root)->win);
  if (ws->cur_w == w)
    ws->cur_w = 0;
  if (ws->new_cur_w == w)
    ws->new_cur_w = 0;
  if (!NULL(w->lines))
    {
      l = XLINE(w->lines);
      while (l)
	{
	  p = l;
	  if (ws->cur_line == l)
	    { ws->cur_line = 0; ws->cur_char = 0; }
	  if (ws->new_cur_line == l)
	    { ws->new_cur_line = 0; ws->new_cur_char = 0;}
	  l = l->next;
	  free_line(p);
	}
    }
  w->lines = Qnil;
  if (!NULL(w->modeline))
    {
      l = XLINE(w->modeline);
      if (l)
	{
	  free_line(l);
	}
    }
  w->modeline = Qnil;
  return;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
mark_all_windows_display_accurate(flag)
     int flag;
{
  register Lisp_Object pw;
  register struct Root_Block *rb = root;

  do
    {
      while (((pw = XWINDOW(rb->ewin)->parent) != rb->ewin) && !NULL(pw))
	rb->ewin = pw;
      mark_window_display_accurate(rb->ewin,flag);
      rb = XROOT(rb->next);
    }
  while (rb != root);
}

mark_window_display_accurate(window,flag)
     Lisp_Object window;
     int flag;
{
  register struct window *w;
  
  for (; !NULL(window); window = w->next)
    {
      w = XWINDOW(window);

      if (!NULL(w->buffer))
	XFASTINT(w->last_modified)
	  = !flag ? 0 : BUF_MODIFF(XBUFFER(w->buffer));
      w->window_end_valid = Qt;
      w->update_mode_line = Qnil;

      if (!NULL(w->vchild))
	mark_window_display_accurate(w->vchild,flag);
      if (!NULL(w->hchild))
	mark_window_display_accurate(w->hchild,flag);
    }
  if (flag)
    {
      last_arrow_position = Voverlay_arrow_position;
      last_arrow_string = Voverlay_arrow_string;
    }
  else
    {
      last_arrow_position = Qt;
      last_arrow_string = Qt;
    }
}     
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if 0
layout_all_windows(rb)
     struct Root_Block *rb;
{
  Lisp_Object window = rb->ewin;

  layout_windows(window);
}
#endif
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
layout_windows(window)
     Lisp_Object window;
{
  for (; !NULL(window); window = XWINDOW(window)->next)
    layout_window(window,0);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
layout_window(window,just_this_one)
     Lisp_Object window;
     int just_this_one;
{
  register struct window *w = XWINDOW(window);
  struct Root_Block *rb = XROOT(w->root);
  struct W_Screen *ws = XWSCREEN(XROOT(w->root)->win);
  int height,opoint;
  struct buffer *old = current_buffer;
  register int width = XFASTINT (w->pixwidth) - 1
    - (XFASTINT (w->pixwidth) + XFASTINT (w->pixleft) != ws->pixw);
  struct position pos;
  register int startp;
  register int hscroll = XINT (w->hscroll);
  int tem,force = 0;
  int first=0;

  if (ws->height == 0) abort();

  /* If this is a combination window, do its children; that's all.  */

  if (!NULL (w->vchild))
    {
      layout_windows (w->vchild);
      return;
    }
  if (!NULL (w->hchild))
    {
      layout_windows (w->hchild);
      return;
    }
  /*
   * If minibuffer is in a distinct screen, this is the minibuffer window,
   * and this isn't the minibuffer screen, then return (silently fail).
   */
  if (distinct_minibuffer && EQ(window,minibuf_window) &&
      rb != mini_root)
    return;
  /*
   * If this is one of (potentially) many minibuffers, then synchronize
   * prior to doing layout.
   */
  if (!distinct_minibuffer && EQ(window,rb->minibuf_window))
    {
      if (!EQ(window,minibuf_window))
	{
	  if (!NULL(Vsynchronize_minibuffers))
	    {
	      /* We've got a duplicate minibuffer window & need to
	       * synchronize it
	       */
	      sync_minibuffer(window);
	    }
	  else
	    {
	      /* We've got a duplicate minibuffer and need to blow off
	       * doing any redisplay of its contents
	       */
	      struct line_header *l = XLINE(w->lines);
	      struct buffer *b = XBUFFER(w->buffer);
	      if (l->body != l->end)
		{
		  free_blocks(l->body,l->end->prev);
		}
	      l->ascent = XFASCENT(XXFONT(STYLE_FIELD(w,font,stylenorm)));
	      l->descent = XFDESCENT(XXFONT(STYLE_FIELD(w,font,stylenorm)));
	      l->ypos = w->pixtop + l->ascent;
	      l->body = l->end;
	      l->end->xpos = w->pixleft;
	      l->chars = 0;
	      l->changed = 1;
	      list_ptr = 0;
	      BLOCK_TYPE(list_ptr) = LINE;
	      BLOCK_LINE(list_ptr) = l;
	      BLOCK_LINE_START(list_ptr) = l->body;
	      BLOCK_LINE_END(list_ptr) = 0;
	      BLOCK_LINE_CLEAR(list_ptr) = 1;
	      goto do_update;
	    }
	}
    }
  
  if (NULL (w->buffer))
    abort ();			/* No buffer...we're screwed */

  if (update_mode_lines)
    w->update_mode_line = Qt;

  /* Stack is empty initially */
  list_ptr = -1;
  /* Set up data on this window; select its buffer and point */
  height = window_char_height(w);
  if (!EQ(window,rb->minibuf_window)) height--;
  if (w == XWINDOW(rb->minibuf_window) && echo_area_contents)
    return;

  current_buffer = XBUFFER(w->buffer);

  if (!just_this_one
      && current_buffer == XBUFFER(XWINDOW(selected_window)->buffer))
    buffer_shared++;

  opoint = point;

  if (!just_this_one && !NULL(current_buffer->force_redisplay))
    {
      force = 1;
    }
  
  if (!EQ (window,selected_window))
    {
      SET_PT(marker_position(w->pointm));
      if (point < BEGV)
	point = BEGV;
      else if (point > ZV)
	point = ZV;
    }

  if (XMARKER(w->start)->buffer != current_buffer)
    goto recenter;

  startp = marker_position(w->start);

  if (!NULL(w->force_start))
    {
      w->update_mode_line = Qt;
      w->force_start = Qnil;
      w->start_button = get_start_button(marker_position(w->start),w);
      XFASTINT(w->last_modified) = 0;
      try_layout(window,startp);
      first++;
      if (point_vpos < 0)
	{
	  /* If point does not appear, move point so it does appear */
	  /* New interface */
	  pos = *compute_motion (startp, 0,
				 ((EQ (window, rb->minibuf_window) && startp == 1)
				  ? minibuf_prompt_width : 0)
				 +
				 (hscroll ? 1 - hscroll : 0),
				 ZV, height / 2,
				 - (1 << (SHORTBITS - 1)),
				 hscroll, pos_tab_offset (w, startp),w,
				 w->start_button); 
	  SET_PT (pos.bufpos);
	  if (w != XWINDOW (selected_window))
	    Fset_marker (w->pointm, make_number (point), Qnil);
	  else
	    /* We want to change point permanently, so don't restore the
	     * old value.
	     */
	    opoint = point;

	  if (w == XWINDOW(selected_window))
	    {
	      int i;
	      struct line_header *l = XLINE(w->lines);
	      struct char_block *cb = 0;
	      ws->cursor_x = max (0, pos.hpos);
	      ws->cursor_y = pos.vpos;
  	      /* Need to find cur_line, cur_char, etc. */
	      i = ws->cursor_y;
	      while (l && i--) l = l->next;
	      if (l)
		cb = l->body;
	      i = ws->cursor_x + (w->pixleft != ws->in_border);
	      while (cb && i--) cb = cb->next;
	      ws->new_cur_w = w;
	      ws->new_cur_line = l;
	      ws->new_cur_char = cb;
	    }
	}
      goto done;
    }
  /* New check...if window height has changed due to font change, etc.
   * and cursor is now off the screen, recenter window around the cursor.
   */
  if (window == selected_window &&
      ws->vis_ypos >= height)
    goto recenter;
  if (force) XFASTINT(w->last_modified) = 0;
  
  if (XFASTINT(w->last_modified) >= MODIFF
      && point >= startp && !clip_changed /* && !force */
      && just_this_one
      && !EQ(w,XWINDOW(rb->minibuf_window)))
    {
      /* No text has changed...only cursor has moved, and
       * it hasn't moved off the screen.
       */
      if (point == w->last_point)
	{
	  /* If cursor hasn't actually moved, don't do anything
	   * (compute_motion is expensive in this context)
	   */
#if 0	  
	  fprintf(stderr,"Bong\n");
#endif	  
	  goto done;
	}
      else
	{
	  pos = *compute_motion(startp,0,(hscroll ? 1-hscroll : 0),
				point,height + 1, 10000,hscroll,
				pos_tab_offset(w,startp),w,
				w->start_button);
	  if (pos.vpos < height)
	    {
	      if (EQ(window,selected_window))
		{
		  int i;
		  struct line_header *l = XLINE(w->lines);
		  struct char_block *cb;
	  
		  /* Point is still on screen */
		  ws->cursor_x = max(0,pos.hpos); 
		  ws->cursor_y = pos.vpos;
#if DEBUG	  
		  fprintf(stderr,"Found cursor at %d,%d\n",ws->cursor_x,ws->cursor_y);
#endif	  
		  /* find new cur_line, cur_char, etc. */
		  i = ws->cursor_y;
		  while (l && i--) l = l->next;
		  if (!l) { first++; goto recenter;}
		  cb = l->body;
		  i = ws->cursor_x + (w->pixleft != ws->in_border);
		  while (cb && i--) cb = cb->next;

		  if (!cb) {first++; goto recenter;}
		  ws->new_cur_w = w;
		  ws->new_cur_line = l;
		  ws->new_cur_char = cb;
		}
	      goto done;
	    }
	  else
	    {
	      first++;
	    }
	}
    }
  else if (!NULL(w->start_at_line_beg) &&
	   !(startp == BEGV || FETCH_CHAR (startp - 1) == '\n'))
    {
      /* Current starting point was at beginning of a line, but no longer
       * is; find a new starting point
       */
      goto recenter;
    }
/*  else if (!EQ(window,rb->minibuf_window) */
  
  else if (just_this_one && !EQ(window,rb->minibuf_window)
	   && point >= startp
	   && XFASTINT(w->last_modified)
	   && !NULL(w->window_end_valid)
	   && (!clip_changed ||
	       (clip_changed && clip_buffer != current_buffer))
	   && !blank_end_of_window
	   && XFASTINT(w->pixwidth) == ws->pixw
	   && EQ(last_arrow_position,Voverlay_arrow_position)
	   && EQ(last_arrow_string,Voverlay_arrow_string)		 
	   && (tem = try_layout_id(window))
	   && tem != -2)
    {
      if (tem > 0)
	goto done;
    }
  else if ((!NULL(w->size_change) ||
	    (startp >= BEGV && startp <= ZV &&
	     (startp < ZV || startp == BEGV ||
	      (XFASTINT(w->last_modified) >= MODIFF)))))
    {
      try_layout(window,startp);
      first++;
      if (point_vpos > -1)
	goto done;
    }
  
  XFASTINT(w->last_modified) = 0;
  w->update_mode_line = Qt;

  /* Try to scroll by a few lines */
  if (scroll_step && !clip_changed)
    {
      if (point > startp)
	{
	  pos = *vmotion (Z - XFASTINT(w->window_end_pos),
			  scroll_step,hscroll,window);
	  if (pos.vpos >= height)
	    goto scroll_fail;
	}
      pos = *vmotion(startp,point < startp ? -scroll_step : scroll_step,
		     hscroll,window);
      if (point >= pos.bufpos)
	{
	  try_layout(window,pos.bufpos);
	  if (point_vpos >= 0)
	    {
	      goto done;
	    }
	}
      scroll_fail: ;
    }
  
 recenter:
  /* Find a start position which centers point */
  pos = *vmotion (point, - height / 2, hscroll, window);
 retry:
  /*
   * NOTE:  If FIRST is non-zero, we've already tried laying out window
   * contents, and have lost incremental layout information.  Set flag
   * so that everything is redrawn and no stray chars are left
   */
/*  w->start_button = get_start_button(pos.bufpos,w); */
  tem = ws->replot_lines;
  if (first) ws->replot_lines = 1;
  try_layout(window, pos.bufpos);
  if (point_vpos == -1 && first < 3)
    {
      /* Ugly; cursor still isn't visible on screen
       * Try scrolling down until cursor is visible.
       * Otherwise, fail.
       */
      first++;
      pos = *vmotion (pos.bufpos, 1, hscroll, window);
      goto retry;
    }
  if (first) ws->replot_lines = tem;
  startp = marker_position (w->start);
  w->start_at_line_beg = 
    (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
 done:
  /*
   * Do the window's mode line if appropriate.  
   */
  if (!EQ(window,rb->minibuf_window) && (!NULL(w->update_mode_line) ||
					 !NULL(w->size_change)))
    {
      layout_mode_line(w);
    }

  if (old != XBUFFER(w->buffer))
    old->force_redisplay = Qnil;
  SET_PT(opoint);
  current_buffer = old;

  /*
   * Window's display structure is accurate.  Stack contains requests
   * to be replotted (and possibly erased in prev pos).
   */
 do_update:

  if (interrupt_input)
    unrequest_sigio();
  stop_polling();

  update_window(w);

  if (interrupt_input)
    request_sigio();
  start_polling();
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return 1 if successful, 0 if clueless, -1 to find a new window start,
 * -2 to do normal redisplay.
 */
int
try_layout_id(window)
     Lisp_Object window;
{
  register struct window *w = XWINDOW(window);
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
#if OLD  
  register int height = XFASTINT(w->height);
#endif
  register int height = window_char_height(w);
  register int pos;
  register int vpos;
  int start = marker_position(w->start);
  int yend = w->pixtop + w->pixheight - (XLINE(w->modeline)->ascent +
					 XLINE(w->modeline)->descent);
  int width = window_char_width(w);

  int hscroll = XINT(w->hscroll);
  int lmargin = hscroll > 0 ? 1 - hscroll : 0;
  register int i,tem;
  int last_text_vpos = 0;
  int stop_vpos;

  struct position val,bp,ep,xp,pp;
  int scroll_amount = 0;
  int delta;
  int tab_offset, epto;
  extern int DEBUG_ALL;

  struct line_header *l;
  
  if (!EQ(window,rb->minibuf_window)) height--;
  
  if (GPT - BEG < beg_unchanged)
    beg_unchanged = GPT - BEG;
  if (Z - GPT < end_unchanged)
    end_unchanged = Z - GPT;

  if (beg_unchanged + 1 < start)
    return 0;			/* Give up if changes go above window */

  bp = *compute_motion (start,0,lmargin,
			beg_unchanged+1,10000,10000,
			hscroll,pos_tab_offset(w,start),w,Qnil);
  if (bp.vpos > height)
    {
      if (point < bp.bufpos && !bp.contin)
	{
	  /* All changes are below the screen, and point is on screen.
	     Don't need to change the screen at all, but do need to
	     update window_end_pos */
	  bp = *compute_motion(start,0,lmargin,
			       Z, height, 0,
			       hscroll,pos_tab_offset(w,start),w,Qnil);
	  XFASTINT(w->window_end_vpos) = height;
	  XFASTINT(w->window_end_pos) = Z - bp.bufpos;
	  return 1;
	}
      return 0;
    }

  vpos = bp.vpos;

  bp = *vmotion(bp.bufpos,0,hscroll,window);

  pos = bp.bufpos;
  val.hpos = lmargin;
  if (pos < start)
    return -1;

  /* If about to start displaying at beginning of continuation line,
   * really start w/ prev line.
   */
  if ((bp.contin && bp.bufpos - 2 == beg_unchanged && vpos > 0)
      ||
      (XTYPE(current_buffer->selective_display) == Lisp_Int
       && XINT(current_buffer->selective_display) > 0
       && bp.bufpos - 2 == beg_unchanged && vpos > 0))
    {
      bp = *vmotion(bp.bufpos,-1,hscroll,window);
      --vpos;
      pos = bp.bufpos;
    }
  if (bp.contin && bp.hpos != lmargin)
    {
#ifdef DEBUG_WRAPPED      
      char ch1,ch2;
#endif      
      int p1,p2;
      
      /* FUDGE */
#ifdef DEBUG_WRAPPED
      ch1 = FETCH_CHAR(pos);
      ch2 = FETCH_CHAR(pos - 1);
#endif
      p1 = current_column(pos);
      p2 = current_column(pos - 1);

      val.hpos = p2 - p1 + 1 + lmargin;
      pos--;      
#if 0
      /* Original code was as follows:  This is no longer a valid
       * calculation of val.hpos since the line conceivably has more or less
       * characters than width (if proportional fonts)
       */
      val.hpos = bp.prevhpos - (width - 1) + lmargin;
      pos--;

#endif  
  
    }

  bp.vpos = vpos;
  
  /* Find first visible newline after which no more is changed */
  tem = find_next_newline(Z - max(end_unchanged, Z-ZV),1);

  if (XTYPE(current_buffer->selective_display) == Lisp_Int
      && XINT(current_buffer->selective_display) > 0)
    while (tem < ZV - 1
	   && (position_indentation (tem)
	       >= XINT(current_buffer->selective_display)))
      tem = find_next_newline(tem,1);

  /* Compute the cursor position after that newline */
  ep = *compute_motion(pos,vpos,val.hpos,tem,height,
		       -(1 << (SHORTBITS - 1)),
		       hscroll,pos_tab_offset(w,bp.bufpos),w,Qnil);

  /* If changes reach past the text available on screen, just
     display rest of screen */

  if (ep.bufpos > Z - XFASTINT(w->window_end_pos))
    stop_vpos = height;
  else
    stop_vpos = ep.vpos;

  if (stop_vpos == ep.vpos
      && (ep.bufpos == BEGV
	  || FETCH_CHAR(ep.bufpos - 1) != '\n'
	  || ep.bufpos == Z - end_unchanged))
    stop_vpos = ep.vpos + 1;

  next_button = w->start_button;
  point_vpos = -1;
  point_v = 0;
  point_h = 0;

  if (stop_vpos < height)
    {
      epto = pos_tab_offset(w,ep.bufpos);


      xp = *compute_motion(ep.bufpos,ep.vpos,ep.hpos,
			   Z - XFASTINT(w->window_end_pos),
			   10000,0,
			   hscroll,epto, w,Qnil);

      scroll_amount = xp.vpos - XFASTINT(w->window_end_vpos);
	  
      /* Is everything on screen below changes whitespace?
	 If so, blow off scrolling */
      for (i = ep.bufpos; i < xp.bufpos; i++)
	{
	  tem = FETCH_CHAR(i);
	  if (tem != ' ' && tem != '\n' && tem != '\t')
	    break;
	}
      if (i == xp.bufpos)
	return -2;

      XFASTINT(w->window_end_vpos) += scroll_amount;

      /* Before doing any scrolling, verify that point will be on screen */
      if (point > ep.bufpos && !(point <= xp.bufpos && xp.bufpos < height))
	{
	  if (point <= xp.bufpos)
	    {
	      pp = *compute_motion(ep.bufpos,ep.vpos,ep.hpos,
				   point,height,
				   -(1 << (SHORTBITS -1)),
				   hscroll,epto,w,Qnil);
	    }
	  else
	    {
	      pp = *compute_motion(xp.bufpos,xp.vpos,xp.hpos,
				   point,height,
				   -(1 << (SHORTBITS -1)),
				   hscroll,pos_tab_offset(w,xp.bufpos),w,Qnil);
	    }
	  if (pp.bufpos < point || pp.vpos == height)
	    return 0;
	  point_vpos = pp.vpos;
	  point_hpos = pp.hpos + (w->pixleft != ws->in_border);
	}

      list_ptr = -1;

#if 0
      if (DEBUG_ALL)
	print_window(w);
#endif
      old_cur_char.ch = ws->cur_char->ch;
      old_cur_char.xpos = ws->cur_char->xpos;
      old_cur_char.style = ws->cur_char->style;

      old_cur_line.chars = ws->cur_line->chars;
      old_cur_line.ypos = ws->cur_line->ypos;
      old_cur_line.ascent = ws->cur_line->ascent;
      old_cur_line.descent = ws->cur_line->descent;      
      
      if (stop_vpos - scroll_amount >= height
	  || ep.bufpos == xp.bufpos)
	{
	  if (scroll_amount < 0)
	    stop_vpos -= scroll_amount;
	  scroll_amount = 0;
	  blank_end_of_window = 1;
	}
      else if (!scroll_amount)
	{}
      else if (bp.bufpos == Z - end_unchanged)
	{
	  next_button = w->start_button;	      
	  tem = scroll_screen_lines(bp.vpos - scroll_amount,
				    height - max(0,scroll_amount),
				    scroll_amount,w);
	  if (tem < 0)
	    {
	      /* Don't know how, but scrolling info is garbaged wrt the
	       * actual display structs.  Fail and do a regular layout.
	       */
	      return -2;
	    } 
	}
      else if (scroll_amount)
	{
	  next_button = w->start_button;	  
	  tem = scroll_screen_lines(ep.vpos - scroll_amount,
				    height - max(0,scroll_amount),
				    scroll_amount,w);

	  if (tem < 0)
	    {
	      /* Don't know how, but scrolling info is garbaged wrt the
	       * actual display structs.  Fail and do a regular layout.
	       */
	      return -2;
	    }
	}
    }

#if 0
  if (DEBUG_ALL)
    print_window(w);
#endif

  if (stop_vpos >= height)
    { 
      return -2;
    }
#if 0
  if (stop_vpos >= height)
    {
      stop_vpos = height;
      scroll_amount = 0;
    }
#endif
  if (vpos == 0 && pos < marker_position(w->start))
    Fset_marker(w->start,make_number(pos),Qnil);

  last_text_vpos = vpos;

  tab_offset = pos_tab_offset(w,pos);
  if (val.hpos + hscroll - (hscroll > 0) < 0)
    tab_offset += width - 1;

  /* Layout lines that were inserted */

  {
    int propogation = 0;
    int ypos;

    if (vpos < stop_vpos && vpos < height)
      {
	i = vpos;
	l = XLINE(w->lines);
	while (l && i--) l = l->next;
	if (l)
	  ypos = l->prev ? (l->prev->ypos + l->prev->descent) : w->pixtop;

	while (vpos < stop_vpos)
	  {
	    val = *layout_text_line(w,vpos,pos,val.hpos,
				    tab_offset,&propogation,l);

	    propogation = 0;
	    l->shifted = 0;
	    tab_offset += width - 1;
	    if (val.vpos) tab_offset = 0;

	    l->prevy = l->ypos;
	    l->ypos = ypos + l->ascent;
	    ypos += (l->ascent + l->descent);
	    vpos++;
	    if (pos != val.bufpos)
	      last_text_vpos
		= vpos - (val.vpos && (FETCH_CHAR (val.bufpos - 1) != '\n'));
	    pos = val.bufpos;

	    l = next_line(l);
	  }
      }
    else
      {
	l = 0;
	ypos = 0;
      }
    if (vpos == height && l)
      {
	l->prev->next = 0;
	free_line(l);
      }
    /* Now update y positions for remaining lines, and possibly clip
     * lines at end of window
     */
    if (scroll_amount > 0)
      {
	while (ypos < yend)
	  {
	    if (!l) break;
	    
	    if ((ypos + l->ascent + l->descent) > yend)
	      {
		/* Remaining lines can be clipped */
#if 0
		if (l->prev) l->prev->next = 0;

		if (BLOCK_TYPE(0) == BLIT)
		  BLOCK_BLIT_END(0) = l->prev;
		 
		free_lines(l);
#endif		
		break;
	      }
	    l->prevy = l->ypos;
	    l->ypos = ypos + l->ascent;
	    ypos += (l->ascent + l->descent);

	    l = l->next;
	  }

	if (l && l != XLINE(w->lines))
	  {
	    if (BLOCK_TYPE(0) == BLIT)
	      BLOCK_BLIT_END(0) = l->prev;
	    
	    if (l->prev) l->prev->next = 0;
	    free_lines(l);
	  }
      }	
  }

 if (vpos == height)
   {
     if (val.hpos < lmargin)
       val.bufpos++;
     XFASTINT(w->window_end_vpos) = last_text_vpos;
     XFASTINT(w->window_end_pos) = Z - val.bufpos;
   }

  if (scroll_amount < 0)
    {
      struct line_header *l,*prev;
      int ypos, last_bufpos;
      int propogation = 0;
            
      vpos = xp.vpos;
      pos = xp.bufpos;
      last_bufpos = pos;

      BLOCK_BLIT_START(0)->prevy = BLOCK_BLIT_OLD_TOP(0);

      val.hpos = lmargin;
      if (pos == ZV)
	vpos = height + scroll_amount;
      else if (xp.contin && xp.hpos != lmargin)
	{
	  val.hpos = xp.prevhpos - width + lmargin;
	  pos--;
	}

      blank_end_of_window = 1;
      tab_offset = pos_tab_offset(w,pos);
      if (val.hpos < 0)
	tab_offset += width - 1;

      prev = BLOCK_BLIT_END(0);
      l = next_line(prev);
      ypos = prev->ypos + prev->descent;

      if (val.hpos < 0)
	tab_offset += width - 1;

      while (ypos < yend)
	{
	  val = *layout_text_line(w,vpos,pos,val.hpos,
				  tab_offset,&propogation,l);

	  if (w->pixleft == ws->in_border && last_bufpos == val.bufpos)
	    {
	      if (point_vpos == vpos) point_vpos = -1;
	      if (BLOCK_LINE(list_ptr) == l) list_ptr--;	      
	      goto window_done;
	    }

	  tab_offset += width - 1;
	  if (val.vpos) tab_offset = 0;
	  if ((ypos + l->ascent + l->descent) > yend)
	    {
	      if (point_vpos == vpos) point_vpos = -1;
	      if (BLOCK_LINE(list_ptr) == l) list_ptr--;
	      goto window_done;
	    }

	  l->prevy = l->ypos;
	  l->ypos = ypos + l->ascent;
	  ypos += (l->ascent + l->descent);
	  vpos++;
	  if (pos != val.bufpos)
	    last_text_vpos
	      = vpos - (val.vpos && (FETCH_CHAR(val.bufpos - 1) != '\n'));
	  pos = val.bufpos;

	  prev = l;
	  l = next_line(l);
	  last_bufpos = val.bufpos;
	}

     window_done:

      if (l && l != XLINE(w->lines) && w->pixleft == ws->in_border)
	{
	  free_lines(l);
	  if (prev) prev->next = 0;
	}
      
      if (xp.bufpos == ZV
	  && xp.bufpos == point)
	point_vpos = -1;
      
    }

  i = 1;
  l = XLINE(w->lines);
  while (l->next) { l = l->next; i++; }

  XFASTINT(w->window_end_ppos) = l->ypos + l->descent;
  XFASTINT(w->height) = i;	/* # lines in window */

  if (yend - (l->ypos + l->descent) > 0)
    {
      list_ptr++;
      BLOCK_TYPE(list_ptr) = AREA;
      BLOCK_AREA_TOP(list_ptr) = l->ypos + l->descent;
      BLOCK_AREA_BOTTOM(list_ptr) = yend;
    }

  if (scroll_amount)
    {
      delta = height - xp.vpos;
      if (delta < 0
	  || (delta > 0 && xp.bufpos < ZV)
	  || (delta == 0 && xp.hpos))
	{
	  val = *vmotion(Z - XFASTINT(w->window_end_pos),
			 delta,hscroll,window);
	  XFASTINT(w->window_end_pos) = Z - val.bufpos;
	  XFASTINT(w->window_end_vpos) += val.vpos;
	}

/*      ws->cur_char = &old_cur_char; */
/*      ws->cur_line = &old_cur_line; */
    }

  if (list_ptr > -1 && w == XWINDOW(selected_window))
    {
      ws->cur_char = &old_cur_char;
      ws->cur_line = &old_cur_line;
    }

  w->window_end_valid = Qnil;

  if (point_vpos < 0)
    {
      val = *compute_motion(start,0,lmargin,
			    point, 10000, 10000,
			    hscroll,pos_tab_offset(w,start),w,Qnil);
      if (val.vpos >= height)
	{
	  /* Everything for this window is hosed */
	  ws->replot_lines = 1;
	    
	  return 0;		/* Fail */
	}

      point_vpos = val.vpos;
      point_hpos = val.hpos + (w->pixleft != ws->in_border);
    }

  /* Find cursor stuff */
  ws->cursor_x = max(0,point_hpos);
  ws->cursor_y = point_vpos;
  {
    int i;
    struct line_header *l = XLINE(w->lines);
    struct char_block *cb = 0;

    i = ws->cursor_y;
    while (l && i--) l = l->next;
    if (l)
      cb = l->body;
    i = ws->cursor_x;
    while (cb && i--) cb = cb->next;

    ws->new_cur_w = w;
    ws->new_cur_line = l;
    ws->new_cur_char = cb;
  }

  return 1;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Given WINDOW's current display structures and current buffer contents
 * begining at POS, produce a stack of "blocks" to plot (these will be a
 * full line at most), and update the display structures including attributes
 * and dependencies.  layout_text_line() is responsible for pushing "blocks"
 * on stack.
 */

try_layout(window,pos)
     Lisp_Object window;
     register int pos;
{
  register struct window *w = XWINDOW(window);
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  struct line_header *l,*prev = 0;
  register int vpos = 0,ypos = w->pixtop,yend = w->pixtop + w->pixheight;
  register int last_text_vpos = vpos;
  register int last_bufpos;
  int width = window_char_width(w);
  struct position val,save_val;
  int tab_offset = pos_tab_offset(w,pos);
  int propogation = 0;
  int t = 0;

  if (rb == cur_root && w == XWINDOW(rb->select) && ws->cur_char && ws->cur_line)
    {
      /* If window is on current screen and is the selected window,
       * save exact info on clearing cursor from where it is.  Pointing
       * into window's display structs isn't sufficient; the objects pointed
       * to may be freed and allocated with new, different information.  It
       * is ok if this is done repeatedly; subsequent times will be setting
       * values to what they already are.
       *
       * Following this call, ws->new_cur_* will point into the display
       * structs for the new cursor position.  At this point, we are
       * guaranteed to follow this by calling update_cursor(), which will
       * set ws->cur_* to be pointing into the display structs again.
       */
      old_cur_char.ch = ws->cur_char->ch;
      old_cur_char.xpos = ws->cur_char->xpos;
      old_cur_char.style = ws->cur_char->style;

      old_cur_line.chars = ws->cur_line->chars;
      old_cur_line.ypos = ws->cur_line->ypos;
      old_cur_line.ascent = ws->cur_line->ascent;
      old_cur_line.descent = ws->cur_line->descent;

      ws->cur_char = &old_cur_char;
      ws->cur_line = &old_cur_line;
    }

  point_vpos = -1;		/* Attempt to locate cursor (point) */
  list_ptr = -1;		/* Begin redisplay for a window. */
  last_bufpos = -1;
  val.bufpos = -2;

#if 0
  if (pos != marker_position(w->start) || NULL(w->start_button))
    {
      w->start_button = get_start_button(pos,w);      
      Fset_marker (w->start,make_number(pos),Qnil);

  if (!NULL(w->start_button))
    fprintf(stderr,"Starting button from %d to %d for %d\n",
	    marker_position(XBUTTON(w->start_button)->start),
	    marker_position(XBUTTON(w->start_button)->end),
	    pos);

    }
#endif
  if (pos != marker_position(w->start))
    {
      w->start_button = get_start_button(pos,w);
      Fset_marker(w->start,make_number(pos),Qnil);
    }

  next_button = w->start_button;
      
  if (!EQ(window,rb->minibuf_window) && XLINE(w->modeline)->ascent == 0)
    {
      int old_list_ptr = list_ptr;

      layout_mode_line(w);
      yend -= (XLINE(w->modeline)->ascent + XLINE(w->modeline)->descent);
      w->modeline = Qnil;      

      /* We used to assume list_ptr was always incremented in
         layout_mode_line.  We no longer assume this, so we no
         longer decrement list_ptr; instead, just use previous
         value of list_ptr.  Thanks to rowley@epx.cis.umn.edu 
         (Henry A. Rowley). */
      /* list_ptr--; */

      list_ptr = old_list_ptr;
    }
     
  if (!NULL(w->modeline)) yend -= (XLINE(w->modeline)->ascent +
				   XLINE(w->modeline)->descent);

  if (NULL(w->lines))
    {
      l = get_line();
      XSET(w->lines,Lisp_Raw_Data,l);
    }
  l = XLINE(w->lines);

  val.hpos = XINT(w->hscroll) ? 1 - XINT(w->hscroll) : 0;
  
  while (ypos < yend)
    {
      save_val.hpos = val.hpos;
      
      val = *layout_text_line(w,vpos,pos,val.hpos,tab_offset,&propogation,l);
      tab_offset += width - 1;
      if (val.vpos) tab_offset = 0;
      
      if ((ypos + l->ascent + l->descent) > yend)
	{
	  /*
 	   * latest line extends past modeline, so ultimately trash it
	   * Be sure it doesn't accidently get redisplayed...
	   * If cursor was found on this line, mark it so that it wasn't,
	   * so window will scroll appropriately.
	   */
	  val.hpos = save_val.hpos;
	  if (point_vpos == vpos) point_vpos = -1;
	  if (BLOCK_LINE(list_ptr) == l) list_ptr--;
	  goto layout_done;
	}

      /*
       * Line fits, so set it's y position relative to screen (0,0)
       * in pixels.
       */
      l->prevy = l->ypos;
      l->ypos = ypos + l->ascent;
      ypos += (l->ascent + l->descent);
      vpos++;
      if (pos != val.bufpos)
	last_text_vpos
	  = vpos - (val.vpos && (FETCH_CHAR (val.bufpos - 1) != '\n'));
      pos = val.bufpos;
      
      prev = l;
      l = next_line(l);      

      if (w->pixleft == ws->in_border && last_bufpos == val.bufpos)
	{
	  break;
	}
      last_bufpos = val.bufpos;
    }
 layout_done:

  /*
   * If remaining lines currently exist in display structures for this
   * window, then something has changed (window height, etc.), so remove
   * them.
   */
  if (l && l != XLINE(w->lines) && w->pixleft == ws->in_border)
    {
      free_lines(l);
      if (prev) prev->next = 0;
    }

  /*
   * Include split character in text on window if last line is split in
   * middle of a character
   */
  if (val.hpos < (XINT(w->hscroll) ? 1 - XINT(w->hscroll) : 0))
    pos++;

  /* Clear to end of window if window size has changed, other dire conditions,
   * or distance to end of window has decreased
   */
  if ((yend - ypos) > 0)
    {
      list_ptr++;
      BLOCK_TYPE(list_ptr) = AREA;
      BLOCK_AREA_TOP(list_ptr) = ypos;
      BLOCK_AREA_BOTTOM(list_ptr) = yend;
    }

  XFASTINT(w->height) = vpos; /* # of lines displayed in window */
  XFASTINT(w->window_end_vpos) = last_text_vpos;
  XFASTINT(w->window_end_ppos) = ypos; /* Pixel pos of end of window's text */
  XFASTINT(w->window_end_pos) = Z - pos;
  w->size_change = Qnil;    
  w->window_end_valid = Qnil;	/* Not valid until display completes */

}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Silently update all attributes for all nodes following a change to
 * contents of style(s) in use on this screen.
 */
update_all_attributes(win)
     Lisp_Object win;
{
  for (; !NULL(win) ; win = XWINDOW(win)->next)
    update_window_attributes (XWINDOW(win));
}

update_window_attributes(w)
     struct window *w;
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  struct char_block *cb;
  register struct line_header *l;
  char ac[2];
  struct Lisp_Style *style;
  struct X_font *font;
  register int asc,desc;

  ac[1]=0;
  if (!NULL(w->vchild))
    {
      update_window_attributes(XWINDOW(w->vchild));
      return;
    }
  if (!NULL(w->hchild))
    {
      update_window_attributes(XWINDOW(w->hchild));
      return;
    }

  style = find_style(w,0);	/* Default style for window */
  
  l = XLINE(w->lines);
  while (l)
    {
      asc = desc = 0;
      cb = l->body;
      while (cb && cb != l->end)
	{
	  font = cb->style ?
	    EQ(XTYPE(cb->style->font),Lisp_Raw_Data) ?
	      XXFONT(cb->style->font) : XXFONT(style->font)
		: XXFONT(style->font);
	  ac[0] = cb->ch;
	  cb->width = text_width(font,ac,1);
	  asc = max(asc,XFASCENT(font));
	  desc = max(desc,XFDESCENT(font));
	  cb->xpos = cb->prev ? cb->prev->xpos + cb->prev->width : w->pixleft;

	  cb = cb->next;
	}
      l->ascent = asc; l->descent = desc;
      l->ypos = l->prev ? l->prev->ypos + l->prev->descent + l->ascent :
	w->pixtop + l->ascent;
      
      l = l->next;
    }
  if (!NULL(w->modeline))
    {
      l = XLINE(w->modeline);
      style = find_style(w,2*mode_line_inverse_video);
      asc = desc = 0;
      cb = l->body;
      while (cb && cb != l->end)
	{
	  font = cb->style ?
	    EQ(XTYPE(cb->style->font),Lisp_Raw_Data) ?
	      XXFONT(cb->style->font) : XXFONT(style->font)
		: XXFONT(style->font);
	  ac[0]=cb->ch;
	  cb->width = text_width(font,ac,1);
	  asc = max(asc,XFASCENT(font));
	  desc = max(desc,XFDESCENT(font));
	  cb->xpos = cb->prev ? cb->prev->xpos + cb->prev->width : w->pixleft;

	  cb = cb->next;
	}
      l->ascent = asc; l->descent = desc;
      l->ypos = w->pixtop + w->pixheight - l->descent;
    }
  return;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Macros for adding a character during layout.  Use this global array for
 * passing single-char values into text_width().  (UGLY)
 * NEXT_CHAR() -> only update positional attributes
 * ADD_CHAR -> update everything
 */
/* char ac[2]; */

#define NEXT_CHAR() {\
  if (prev && prev->xpos + prev->width != cb->xpos)\
    {\
      endc = cb;\
      ac[0] = cb->ch;\
      cb->width = text_width(font,ac,1);\
      cb->xpos = prev ? prev->xpos + prev->width : w->pixleft;\
    }\
  cb->prev = prev;\
  prev = cb;\
  cb = cb->next;}

#define ADD_CHAR(letter,flag) {\
  ac[0] = (letter);\
  wid = text_width(font,ac,1);\
  if (flag && (lwidth + wid) > pixright)\
    goto done;\
  cb = next_block(cb,l);\
  cb->ch = (letter);\
  cb->width = wid;\
  cb->xpos = prev ? prev->xpos + prev->width : w->pixleft;\
  cb->style = style;\
  cb->prev = prev;\
  prev = cb;\
  endc = cb;\
  cb = cb->next;\
  l->changed = 1;\
  asc = max(asc,XFASCENT(font));\
  desc = max(desc,XFDESCENT(font));}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Layout a line L of text of window W's buffer.  Line # VPOS in window,
 * and begins at START in the buffer.  L is marked "Changed" if something
 * in its contents has changed between display structs and actual buffer
 * contents.  L is marked "Shifted" if it is unchanged, but is shifting
 * vertically due to vertical dependencies on lines above it in the window.
 * Line is also marked "Changed" if the replot_lines flag has been
 * set for this iteration int he display code.  (Another external dependency)
 * Redisplay requests contain L, and the START and END of changed regions.
 */
struct position val_layout_text_line;

struct position *
layout_text_line(w,vpos,start,hpos,taboffset,propogation,l)
     struct window *w;
     int vpos;
     int start;
     int hpos;
     int taboffset;
     int *propogation;
     struct line_header *l;
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  struct char_block *cb,*prev,*Changed,*t,*cpos,*endc;
  register int pos = start;
  int pause,end,i;
  register unsigned char c,c1;
  register int asc,desc,lwidth,lastpos;
  register int numchars;
  int inc = 0;
  register unsigned char *p;
  char ac[2];
  struct Lisp_Style *style;
  Lisp_Object ob;
  int multi = 0;		/* Last buffer char was a multi-character */
  char replot_lines;
  int wid;
  struct X_font *font;
  int tab_width = XINT(current_buffer->tab_width);
  int ctl_arrow = (!NULL(current_buffer->ctl_arrow))
    + (EQ (current_buffer->ctl_arrow, Qt));
  int meta_printable = !NULL (current_buffer->meta_printable);
  struct position val;
  struct buffer *b = current_buffer;
  int pixright = w->pixleft + w->pixwidth;
  int invis,button_start,button_end,foo = 0;
  int hscroll = XINT(w->hscroll);
  int truncate = hscroll || (truncate_partial_width_windows
			     && XFASTINT (w->pixwidth) < ws->pixw)
    || !NULL (current_buffer->truncate_lines);
  int selective = XTYPE (current_buffer->selective_display) == Lisp_Int
    ? XINT (current_buffer->selective_display)
      : !NULL (current_buffer->selective_display) ? -1 : 0;
  int selective_e = selective &&
    !NULL (current_buffer->selective_display_ellipses);
  unsigned char *overwrite_string, aa;
  int overwrite_count;  

  if (tab_width <= 0 || tab_width > 20) tab_width = 8;

  cb = l->body;
  cb->xpos = w->pixleft;
  replot_lines = ws->replot_lines;
  l->shifted = *propogation && !l->new;	/* Don't shift for new lines */
  l->changed = !NULL(w->size_change) || replot_lines || l->changed;
  l->tabs = 0;
  Changed = 0;
  endc = 0;
  prev = 0;
  cpos = 0;
  numchars = 0;
  lwidth = w->pixleft;		/* Left pixel edge of window */
  overwrite_count = 0;
  overwrite_string = 0;		/* null string */

  /* Mark this line as being "in display" */
  l->in_display = -1;
  
  if (w == XWINDOW(XROOT(w->root)->minibuf_window) && start == 1 && l->prev == 0)
    {
      if (minibuf_prompt)
	{
	  l->lwidth = lwidth;
	  l->chars = numchars;
	  Changed = layout_string_inc(w,minibuf_prompt,0,
				      !truncate ? '\\' : '$',0,pixright,
				      l,0,&hpos,0);
	  minibuf_prompt_width = hpos;
	}
      cb = l->body;
      for (i = 0; i < hpos; i++)
	{
	  /* Assume:  we are skipping over valid characters */
	  lwidth += cb->width;
	  numchars++;
	  prev = cb;
	  cb = cb->next;
	}
    }

  font = XXFONT(STYLE_FIELD(w,font,stylenorm));
  asc = XFASCENT(font); desc = XFDESCENT(font);

  /* Adjust right logical border of window by width of "truncate"
   * character; this is either '\' or '$'
   */
  ac[0] = truncate ? '$' : '\\';
  ac[1] = 0;
  pixright -= text_width(font,ac,1);  

  ob = get_button(current_buffer,pos,&button_start,&button_end,
		  &next_button,0);
  if (!NULL(ob))
    {
      /* Set font according to style's font (if there) */
      style = XSTYLE(ob);
      if (style && (XTYPE(style->font) == Lisp_Raw_Data))
	{
	  font = XXFONT(style->font);
	  asc = max(asc,XFASCENT(font));
	  desc = max(desc,XFDESCENT(font));
	}
    }
  else style = 0;		/* Font unchanged... */

  /*
   * If window's left edge isn't at screen edge, draw window divider '|'
   * on left edge of window.
   */
  if (XFASTINT(w->pixleft) != ws->in_border)
    {
      c = '|';
      if (((unsigned char)(cb->ch) != c) || cb->style != style || Changed)
	{
	  Changed = cb;
	  ADD_CHAR(c,1);
	}
      else
	{
	  NEXT_CHAR();
	}
      lwidth += prev->width;
      /* Don't change pos; buffer position hasn't changed.
       * This also won't constitute the entire line being changed/trashed
       */
    }
  
  numchars = hpos;

  if (hscroll)
    {
      /* Window is horizontally scrolled.  Overwrite '$' over first
       * actually displayed character on the line, if appropriate.
       */
      aa = '$';
      overwrite_string = &aa;
      overwrite_count = 1;
    }

  /* Check if overlay arrow should be displayed on this line */
  if (XTYPE(Voverlay_arrow_position) == Lisp_Marker &&
      current_buffer == XMARKER(Voverlay_arrow_position)->buffer &&
      start == marker_position(Voverlay_arrow_position) &&
      XTYPE (Voverlay_arrow_string) == Lisp_String)
      {

	unsigned char *p = XSTRING(Voverlay_arrow_string)->data;
	int len = XSTRING(Voverlay_arrow_string)->size;
	/* Overwrite first 'len' displayed characters with the overlay
	 * arrow string.
	 */
	overwrite_string = p;
	overwrite_count = len;
      }
  end = ZV;
  pause = pos;  

  /*
   * Layout line based on current contents and buffer contents.
   * Record position of earliest change.
   */
  while (lwidth <= pixright)
    {
      inc = 0;
      if (pos == pause)
	{
	  if (pos == end)
	    break;
	  if (pos == button_start || pos == button_end || button_start == -1)
	    {
	      if (pos == button_end || button_end == -1)
		{
		  /* Possible change in text attribution.  Find appropriate
		   * zone, style, etc.
		   */
		  ob =
		    get_button(current_buffer,pos,&button_start,&button_end,
			       &next_button,0);
		  if (!NULL(ob))
		    {
		      style = XSTYLE(ob);
		      if (style && (XTYPE(style->font) == Lisp_Raw_Data))
			{
			  font = XXFONT(style->font);
			  asc = max(asc,XFASCENT(font));
			  desc = max(desc,XFDESCENT(font));
			}
		      else
			{
			  font = XXFONT(STYLE_FIELD(w,font,stylenorm));
			  asc = max(asc,XFASCENT(font));
			  desc = max(desc,XFDESCENT(font));
			}
		    }
		  else
		    {
		      font = XXFONT(STYLE_FIELD(w,font,stylenorm));
		      asc = max(asc,XFASCENT(font));
		      desc = max(desc,XFDESCENT(font));
		      style = 0;
		    }
		  if (asc != l->ascent || desc != l->descent)
		    l->changed = 1;
		}
	    }

	  /* Find next point where attribution might change
	   * (or gap position, etc.)
	   */
	  pause = ZV;
	  if (pos < point && point < pause)
	    pause = point;
	  if (pos < GPT && GPT < pause)
	    pause = GPT;
	  if (pos < button_start && button_start < pause)
	    pause = button_start;
	  else if (pos < button_end && button_end < pause)
	    pause = button_end;

	  p = &FETCH_CHAR (pos);
	}
      c = *p++;
      multi = 0;
      if ((c >= 040 && c < 0177)
             || (meta_printable && is_meta_char(c)))
	{
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      if (overwrite_count && numchars < overwrite_count)
		c = *overwrite_string++;
	      if ((unsigned char)(cb->ch) != c || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c,1);
		  Changed = Changed ? Changed : prev;
		}
	      else
		{
		  NEXT_CHAR();
		}
	      cpos = prev;
	      inc = 1;
	      lwidth += prev->width;
	    }
	}
      else if (c == '\n')
	{
	  inc = 0;
	  invis = 0;
	  while (pos < end
		 && selective > 0
		 && position_indentation(pos + 1) >= selective)
	    {
	      invis = 1;
	      pos = find_next_newline(pos + 1, 1);
	      if (FETCH_CHAR (pos-1) == '\n')
		pos--;
	    }
	  if (invis && selective_e)
	    {
	      int i;
	      /* Insert ' ...' */
	      c = ' ';
	      multi = 1;
	      if (numchars < 0)
		numchars++;
	      else
		{
		  if (overwrite_count && numchars < overwrite_count)
		    c = *overwrite_string++;
		  if (((unsigned char)(cb->ch) != c) || 
                      (cb->style != style) || replot_lines)
		    {
		      ADD_CHAR(c,1);
		      Changed = Changed ? Changed : prev;
		    }
		  else
		    {
		      NEXT_CHAR();
		    }
		  cpos = prev;
		  inc = 1;
		  lwidth += prev->width;
		}
	      c = '.';
	      for (i = 0; i < 3; i++)
		{
		  if (numchars < 0)
		    numchars++;
		  else
		    {
		      if (overwrite_count && numchars < overwrite_count)
			c = *overwrite_string++;
		      if (((unsigned char)(cb->ch) != c) || 
                          (cb->style != style) ||
			  replot_lines)
			{
			  ADD_CHAR(c,1);
			  Changed = Changed ? Changed : prev;
			}
		      else
			{
			  NEXT_CHAR();
			}
		      inc++;
		      lwidth += prev->width;
		    }

		  c = '.';
		}
	    }
	  numchars += inc;
	  inc = 0;
	  goto done;
	}
      else if (c == '\t')
	{
	  int base = numchars;
	  
	  l->tabs = 1;	  
	  c = ' ';
	  inc = 0;
	  multi = 1;
	  cpos = 0;
	  do
	    {
	      if (numchars + inc < 0)
		;
	      else
		{
		  if (overwrite_count && numchars + inc < overwrite_count)
		    c = *overwrite_string++;
		  if (((unsigned char)(cb->ch) != c) || 
                      (cb->style != style) || replot_lines)
		    {
		      ADD_CHAR(c,1);
		      Changed = Changed ? Changed : prev;
		    }
		  else
		    {
		      NEXT_CHAR();
		    }
		  cpos = cpos ? cpos : prev;
		  lwidth += prev->width;
		  c = ' ';
		}
	      inc++;
	    }
	  while ((numchars + inc + taboffset + hscroll - (hscroll > 0))
		 % tab_width);
	}
      else if (c == Ctl('M') && selective == -1)
	{
	  pos = find_next_newline(pos,1);
	  if (FETCH_CHAR (pos-1) == '\n')
	    pos--;
	  if (selective_e)
	    {
	      int i;
	      /* Insert ' ...' */
	      c = ' ';
	      multi = 1;
	      if (numchars < 0)
		numchars++;
	      else
		{
		  if (overwrite_count && numchars < overwrite_count)
		    c = *overwrite_string++;
		  if ((unsigned char)(cb->ch) != c || (cb->style != style) || 
                      replot_lines)
		    {
		      ADD_CHAR(c,1);
		      Changed = Changed ? Changed : prev;
		    }
		  else
		    {
		      NEXT_CHAR();
		    }
		  cpos = prev;
		  inc = 1;
		  lwidth += prev->width;
		}
	      c = '.';
	      for (i = 0; i < 3; i++)
		{
		  if (numchars < 0)
		    numchars++;
		  else
		    {
		      if (overwrite_count && numchars < overwrite_count)
			c = *overwrite_string++;
		      if (((unsigned char)(cb->ch) != c) || 
                          (cb->style != style) ||
			  replot_lines)
			{
			  ADD_CHAR(c,1);
			  Changed = Changed ? Changed : prev;
			}
		      else
			{
			  NEXT_CHAR();
			}
		      inc++;
		      lwidth += prev->width;
		      c = '.';
		    }
		}
	    }
	  numchars += inc;
	  inc = 0;
	  goto done;
	}
      else if (ctl_arrow == 1 && c >= 040)
	{
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      if (overwrite_count && numchars < overwrite_count)
		c = *overwrite_string++;
	      if (( (unsigned char)cb->ch != c) || (cb->style != style) ||
		  replot_lines)
		{
		  ADD_CHAR(c,1);
		  Changed = Changed ? Changed : prev;
		}
	      else
		{
		  NEXT_CHAR();
		}
	      cpos = prev;
	      inc = 1;
	      lwidth += prev->width;
	    }
	}
      else if (is_control_char(c) && ctl_arrow)
	{
	  multi = 1;
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      c1 = '^';
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      cpos = prev;
	      inc = 1;
	      lwidth += prev->width;
	    }
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      c1 = c ^ 0100;
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      inc++;
	      lwidth += prev->width;
	    }
	}
      else
	{
	  multi = 1;
	  c1 = '\\';
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      cpos = prev;
	      inc = 1;
	      lwidth += prev->width;
	    }
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      c1 = (c>>6) + '0';
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      inc++;
	      lwidth += prev->width;
	    }
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      c1 = (7 & (c >>3)) + '0';
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      inc++;
	      lwidth += prev->width;
	    }
	  if (numchars < 0)
	    numchars++;
	  else
	    {
	      c1 = (7 & c) + '0';
	      if (overwrite_count && numchars < overwrite_count)
		c1 = *overwrite_string++;
	      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c1,1);
		  Changed = Changed ? Changed : prev;	      
		}
	      else
		{
		  NEXT_CHAR();
		}
	      inc++;
	      lwidth += prev->width;
	    }
	}
      if (pos == point && point_vpos < 0)
	{
	  point_vpos = vpos;
	  point_hpos = numchars;
	  point_v = l;
	  point_h = cpos ? cpos : l->body;
	}      
      pos++;
      numchars += inc;
      if (lwidth >= pixright) break;
    }
 done:
  val.hpos = -XINT(w->hscroll);
  if (val.hpos) val.hpos++;

  val.vpos = 1;
  lastpos = pos;

  if (hscroll && numchars <= 0 && lastpos != start && overwrite_count == 1)
    {
      /* Here we have a horizontally scrolled line with some text, but
       * none which was actually layed out.  Layout the '$' in the leftmost
       * position of the window if needed
       */
      c1 = '$';
      if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || Changed)
	{
	  ADD_CHAR(c1,0);
	  Changed = prev;
	}
      else
	{
	  NEXT_CHAR();
	}
      lwidth += prev->width;

      /* If cursor goes here, position it appropriately
       */
      if (point == pos)
	{
	  point_vpos = vpos;
	  point_hpos = 0;
	  point_v = l;
	  point_h = (XFASTINT(w->pixleft) != ws->in_border) ?
	    l->body->next : l->body;
	}
      overwrite_count = 0;
    }

  if (overwrite_count && numchars < overwrite_count &&
      overwrite_string != &aa)
    {
      /* Layout remainder of overlay arrow string, etc.
       * DON'T executed this code for a '$' at left edge of screen
       */
      char c;

      if (numchars < 0) numchars = 0;
      
      while (numchars < overwrite_count)
	{
	  c = *overwrite_string++;

	  if (((unsigned char)(cb->ch) != c) || (cb->style != style) || 
              Changed)
	    {
	      ADD_CHAR(c,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  lwidth += prev->width;
	  numchars ++;
	}
      if (point == pos)
	{
	  point_vpos = vpos;
	  point_v = l;
	  point_h = l->body;
	  point_hpos = 0;
	}
    }

  if (multi)
    {
      /* Continuation in middle of character */
      val.hpos -= inc;
    }
  
  if (lwidth > pixright)
    {
      pos--;
      lwidth -= cb->width;
      numchars--;
    }

  /* Decide where next line begins */
  if (pos < ZV)
    {
      if (FETCH_CHAR (pos) == '\n')
	{
	  pos++;
	}
      else
	{
	  /* Stopped due to right edge of window */
	  if (truncate)
	    {
	      foo++;
	      if (cb != l->end && cb->ch != '$')
		{
		  t = l->end->prev;
		  free_blocks(cb,t);
		  prev->next = l->end;
		  l->end->prev = prev;
		  cb = l->end;
		}
	      c = '$';
	      if (((unsigned char)(cb->ch) != c) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c,0);
		  Changed = Changed ? Changed : prev;
		  endc = prev;
		}
	      else
		{
		  NEXT_CHAR();
		}
	      lwidth += prev->width;
	      /* Truncating, so start next line after next newline.
	       * Point is on this line if it is before the newlinw.
	       */
	      pos = find_next_newline(pos,1);
	      val.hpos = XINT(w->hscroll) ? 1 - XINT(w->hscroll) : 0;
	      lastpos = pos - (FETCH_CHAR (pos -1) == '\n');
	    }
	  else
	    {
	      if (cb != l->end && cb->ch != '\\' )
		{
		  /* Only do this if incrementally necessary */
		  t = l->end->prev;
		  free_blocks(cb,t);
		  prev->next = l->end;
		  l->end->prev = prev;
		  cb = l->end;
		}
	      c = '\\';
	      if (((unsigned char)(cb->ch) != c) || (cb->style != style) || 
                  replot_lines)
		{
		  ADD_CHAR(c,0);
		  Changed = Changed ? Changed : prev;
		  endc = prev;
		}
	      else
		{
		  NEXT_CHAR();
		}
	      lwidth += prev->width;
	      val.vpos = 0;
	      lastpos--;
	    }
	}
    }

  /* Possibility exists that we are clipping all/part of line; free
   * those blocks.
   */
  if (cb != l->end && l->end != l->body)
    {
      t = l->end->prev;
      if (l->body == cb)
	{
	  l->body = l->end;
	}
      free_blocks(cb,t);
      if (!Changed) Changed = l->end;
      l->end->prev = prev;
      if (prev) prev->next = l->end;
      l->changed = 1;
    }

  /* Update point if at EOL or in invisible text. */
  if (start <= point && point <= lastpos && point_vpos < 0)
    {
      point_vpos = vpos;
      point_hpos = numchars;
      point_v = l;
      point_h = cb;
    }
  if (point_vpos == vpos)
    {
      if (point_hpos < 0)
	{
	  point_hpos = 0;
	  point_h = l->body;
	}
      /* hpos of point may not exist anymore. */
      if (point_hpos >= numchars && numchars >= 0)
	{
	  point_hpos = numchars;
	  point_h = l->end;

	  if (truncate && l->end->prev && foo)
	    point_h = l->end->prev;
	}
      if (w == XWINDOW (selected_window))
	{
	  ws->cursor_y = point_vpos;	  
	  ws->cursor_x = point_hpos;
	  ws->new_cur_w = w;
	  ws->new_cur_line = point_v;
	  ws->new_cur_char = point_h;

	  if (hpos == (XINT (w->hscroll) ? 1 - XINT(w->hscroll) : 0)
	      && val.vpos)
	    {
	      this_line_bufpos = start;
	      this_line_buffer = current_buffer;
	      this_line_start_hpos = hpos;
	      this_line_vpos = point_vpos;
	      this_line_line = l;
	      this_line_start_hpos = hpos;
	      this_line_endpos = Z - lastpos;
	    }
	  else
	    {
	      this_line_bufpos = 0;
	    }
	}
    }

  /*
   * Decide on propogation value to pass to lines dependent on this one.
   * Decide on what redisplay action to take for this line:  If propogation
   * is one, then entire line must be replotted; otherwise, attempt to start
   * at position indicated by "Changed" pointer.
   */
  
  *propogation = *propogation || (l->ascent != asc || l->descent != desc);
  if (l->ascent != asc || l->descent != desc || !Changed || *propogation)
    {
      Changed = l->body; endc = l->end;
    }

  if ((replot_lines || !NULL(w->size_change)) ||
      (l->body == l->end && l->new))
    {
      l->changed = 1;
      Changed = l->body;
      endc = 0;
    }

  l->ascent = asc; l->descent = desc;
  l->lwidth = lwidth;
  l->end->xpos = l->end->prev ? l->end->prev->xpos + l->end->prev->width
    : w->pixleft;
  l->chars = (numchars > 0) ? numchars : 0;

  /*
   * If line has changed or is new, push it onto stack for plotting
   */
  if (l->changed || l->shifted)
    {
      list_ptr++;
      BLOCK_TYPE(list_ptr) = LINE;
      BLOCK_LINE(list_ptr) = l;
      BLOCK_LINE_START(list_ptr) = Changed;
      BLOCK_LINE_END(list_ptr) = endc;
      BLOCK_LINE_CLEAR(list_ptr) = 1;
      if (l->in_display != -1)
	{
	  cb = l->body;
	  while (cb)
	    {
	      if (cb->xpos + cb->width >= l->in_display)
		BLOCK_LINE_START(list_ptr) = cb;
	      cb = cb->next;
	    }
	}
    }

  l->in_display = 0;
  val.bufpos = pos;
  val_layout_text_line = val;
  return &val_layout_text_line;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Do incremental layout of STRING for window W, using specified STYLE.
 * HPOS = starting horizontal position
 * TRUNCATE = truncate character (or 0)
 * MINCOL, MAXCOL = minimum start and maximum end column
 * L = line struct to modify
 * STYLE = style for display on line
 * CLIP = 0/1 -> Clip remaining contents of L once STRING is layed out
 * returns pointer to first character of L that has changed.
 */
struct char_block *
layout_string_inc(w,string,hpos,truncate,mincol,maxcol,l,style,ret,clip)
     struct window *w;
     unsigned char *string;
     int hpos;
     char truncate;
     int mincol,maxcol;
     struct line_header *l;
     struct Lisp_Style *style;
     int *ret;
     int clip;     
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  int tab_width = XINT(current_buffer->tab_width);
  int hscroll = XINT(w->hscroll);
  register unsigned char *p;
  char ac[2],replot_lines;
  int pixright = XFASTINT(w->pixleft) + XFASTINT(w->pixwidth);
  struct char_block *cb,*prev,*t,*Changed,*endc;
  register unsigned char c,c1;
  int lwidth = 0;
  int wid,asc,desc,i;
  int numchars;
  struct X_font *font;
  struct buffer *b = XBUFFER(w->buffer);

  if (tab_width <= 0 || tab_width > 20) tab_width = 8;

  if (style && (XTYPE(style->font) == Lisp_Raw_Data))
    font = XXFONT(style->font);
  else
    font = XXFONT(STYLE_FIELD(w,font,stylenorm));

  replot_lines = XWSCREEN(rb->win)->replot_lines;
  Changed = 0;
  endc = 0;
  cb = l->body;
  prev = 0;
  numchars = 0;

  /* Adjust right logical window border depending on
   * width of "truncate" character.
   */
  if (truncate)
    {
      ac[0] = truncate;
      ac[1] = 0;
      pixright -= text_width(font,ac,1);
    }

  if (maxcol >= 0 && mincol > maxcol)
    mincol = maxcol;

  if (maxcol == ws->width) maxcol = 0;

  asc = XFASCENT(font); desc = XFDESCENT(font);

  if (hpos == 0 && w->pixleft != ws->in_border)
    {
      c = '|';
      if (((unsigned char)(cb->ch) != c) || cb->style != style)
	{
	  Changed = cb;
	  ADD_CHAR(c,1);
	}
      else
	{
	  NEXT_CHAR();
	}
      lwidth += prev->width;
    }

  if (hpos > 0)
    {
      for (i = 0; i < hpos; i++)
	{
	  /* ASSUME:  We will always be skipping over valid chars */
	  prev = cb->prev;
	  cb = next_block(cb,l);
	  prev = cb;
	  cb = cb->next;
	  numchars++;
	}
      lwidth = prev->xpos + prev->width;
    }

  while (lwidth <= pixright)
    {
      if (maxcol > 0 && numchars >= maxcol) break;
      c = *string++;
      if (!c) break;
      
      if ((c >= 040 && c < 0177)
          || (buffer_defaults.meta_printable && is_meta_char(c))) 
	{
	  if (((unsigned char)(cb->ch) != c) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	}
      else if (c == '\t')
	{
	  l->tabs = 1;
	  c = ' ';
	  do
	    {
	      if (((unsigned char)(cb->ch) != c) || (cb->style != style) ||
		  replot_lines)
		{
		  ADD_CHAR(c,0);
		  Changed = Changed ? Changed : prev;
		}
	      else
		{
		  NEXT_CHAR();
		}
	      numchars++;
	      lwidth += prev->width;
	    }
	  while ((numchars + hscroll - (hscroll > 0)) % tab_width);
	}
      else if (buffer_defaults.ctl_arrow == 1 && c >= 0x40)
	{
	  if (((unsigned char)(cb->ch) != c) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	}
      else if (is_control_char(c) && buffer_defaults.ctl_arrow)
	{
	  c1 = '^';	      
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	  c1 = c ^ 0100;	      
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	}
      else
	{
	  c1 = '\\';
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	  c1 = (c>>6) + '0';
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	  c1 = (7 & (c>>3)) + '0';
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	  c1 = (7 & c) + '0';
	  if (((unsigned char)(cb->ch) != c1) || (cb->style != style) || 
              replot_lines)
	    {
	      ADD_CHAR(c1,0);
	      Changed = Changed ? Changed : prev;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	  numchars++;
	  lwidth += prev->width;
	}
      if (lwidth > pixright) break;
    }
  done:

  if (c)
    {
      if (truncate)
	{
	  ADD_CHAR(truncate,0);
	  lwidth += prev->width;
	  numchars++;
	  Changed = Changed ? Changed : prev;
	}
    }
  else if (mincol >= 0)
    {
      c = ' ';
      while (numchars < mincol && lwidth < pixright)
	{
	  if (((unsigned char)(cb->ch) != c) || (cb->style != style))
	    {
	      ADD_CHAR(c,0);
	      lwidth += prev->width;
	      numchars++;
	    }
	  else
	    {
	      NEXT_CHAR();
	    }
	}
    }

  if (clip && (cb != l->end))
    {
      t = l->end->prev;
      if (l->body == cb)
	{
	  l->body = l->end;
	}
      free_blocks(cb,t);
      l->end->prev = prev;
      if (prev) prev->next = l->end;
      l->changed = 1;
    }

  l->lwidth = lwidth;
  l->end->xpos = l->end->prev ? l->end->prev->xpos + l->end->prev->width
    : w->pixleft;
  l->changed = l->changed || (l->ascent != asc) || (l->descent != desc);
  l->ascent = max(l->ascent,asc); l->descent = max(l->descent, desc);
  l->chars = numchars;

  *ret = numchars;

  return Changed;
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Layout the modeline for window W.  Unless circumstance are otherwise,
 * attempt to do incrementally - redisplaying only what has changed.
 */
layout_mode_line(w)
     struct window *w;
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  int left = 0;
  int right = ws->pixw; /* ws->width; */
  int ret;
  struct line_header *mode = XLINE(w->modeline);
  struct line_header *new = get_line();
  struct char_block *cb,*cb2,*cb3,*end;
  struct X_font *font;

  if (NULL(w->modeline))
    {
      /* Create mode line space 1st time for window */
      mode = get_line();
      XSET(w->modeline,Lisp_Raw_Data,mode);
    }

  /* Prevent expose code from accessing modeline structures while we are
   * updating it.
   */
  w->modeline = Qnil;
  
  mode_style = find_style(w,2*mode_line_inverse_video);

  new->ascent = 0; new->descent = 0;
  layout_mode_element (w,left,0,right,right,
		       current_buffer->mode_line_format,new,&ret);

  new->changed = 0;
  mode->prevy = mode->ypos;
  mode->ypos = w->pixtop + w->pixheight - mode->descent;
  new->ypos = w->pixtop + w->pixheight - new->descent;
  if ((new->ascent != mode->ascent) || (new->descent != mode->descent) ||
      (mode->prevy != mode->ypos) || (new->lwidth != mode->lwidth) ||
      ws->replot_lines)
    {
      cb = new->body;
      goto endit;
    }
  cb = new->body; cb2 = mode->body;
  while (1)
    {
      if (cb == new->end || cb2 == mode->end) break;
      if ((cb->xpos != cb2->xpos) ||
	  (cb->ch != cb2->ch) ||
	  (cb->width != cb2->width) ||
	  (cb->style != cb2->style))
	{
	  /* Find end of changed region */
	  end = cb;
	  cb3 = cb->next; cb2 = cb2->next;
	  while (1)
	    {
	      if (cb3 == new->end || cb2 == mode->end) break;
	      if ((cb3->xpos != cb2->xpos) ||
		  (cb3->ch != cb2->ch) ||
		  (cb3->style != cb2->style) ||
		  (cb3->width != cb2->width))
		{
		  end = cb3;
		}
	      cb3 = cb3->next; cb2 = cb2->next;
	    }
	  break;
	}
      cb = cb->next; cb2 = cb2->next;
    }
 endit:
  
  if (cb != new->end)
    {
      /* Set window's modeline to reference new structure */
      XSET(w->modeline,Lisp_Raw_Data,new);
      free_line(mode);		/* Junk old contents */
      
      new->changed = 1;
      new->modeline = 1;
      new->shifted = 0;
      if (list_ptr > -1 && BLOCK_TYPE(list_ptr) == AREA)
	{
	  BLOCK_AREA_BOTTOM(list_ptr) = w->pixtop + w->pixheight -
	    (new->ascent + new->descent);
	}
      list_ptr++;
      BLOCK_TYPE(list_ptr) = LINE;
      BLOCK_LINE(list_ptr) = new;
      BLOCK_LINE_START(list_ptr) = cb; /* XLINE(w->modeline)->body; */
      BLOCK_LINE_END(list_ptr) = end;
      BLOCK_LINE_CLEAR(list_ptr) = 0; 
    }
  else
    {
      /* Set window's modeline to reference old structure */
      XSET(w->modeline,Lisp_Raw_Data,mode);
      free_line(new);		/* Junk new copy */
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Layout elements of mode line.  How it translates into text depends on
 * its data type.
 *
 * HPOS is character position where element's text starts.  It is truncated
 * automatically at window edge.
 *
 * DEPTH is depth in recursion.
 *
 * MINENDCOL is minimum character position that element can end.  It will
 * be padded with spaces if needed.
 *
 * MAXENDCOL is maximum character position that element can end at.
 *
 * Returns hpos of end of ELT in RET, and first changed block in line.
 */

int
layout_mode_element (w,hpos,depth,minendcol,maxendcol,elt,l)
     struct window *w;
     int hpos;
     int depth;
     int minendcol;
     register int maxendcol;
     register Lisp_Object elt;
     struct line_header *l;
{
  
 tail_recurse:
  if (depth > 10)
    goto invalid;

  depth++;

#ifdef SWITCH_ENUM_BUG  
  switch ((int) XTYPE (elt))
#else
  switch (XTYPE(elt))
#endif
    {
    case Lisp_Style:
      mode_style = XSTYLE(elt);
      break;
    case Lisp_String:
      {
	/* String...output it and check for %-constructs */
	register unsigned char c;
	register unsigned char *this = XSTRING(elt)->data;

	while (hpos < maxendcol && *this)
	  {
	    unsigned char *last = this;
	    while ((c = *this++) != '\0' && c != '%')
	      ;
	    if (this - 1 != last)
	      {
		register int lim = --this - last + hpos;
		layout_string_inc(w,last,hpos,0,hpos,
				  min(lim,maxendcol),
				  l,mode_style,
				  &hpos,1);
	      }
	    else /* c == '%' */
	      {
		register int spec_width = 0;
		/* We can't allow -ve args due to the "%-" construct.
		 * Argument specifies minwidth but not maxwidth
		 * (maxwidth can be specified by
		 * (<negative-number> . <stuff>) mode-line elements)
		 */
		while ((c = *this++) >= '0' && c <= '9')
		  {
		    spec_width = spec_width * 10 + (c - '0');
		  }

		spec_width += hpos;
		if (spec_width > maxendcol)
		  spec_width = maxendcol;

		if (c == 'M')
		  {
		    hpos =
		      layout_mode_element(w,hpos,depth,
					  spec_width,
					  maxendcol,
					  Vglobal_mode_string,l);
		  }
		else if (c != 0)
		  {
		    layout_string_inc(w,
				      decode_mode_spec(w,c,spec_width-hpos),
				      hpos,0,spec_width,maxendcol,
				      l,mode_style,&hpos,1);
		  }
	      }
	  }
      }
      break;
    case Lisp_Symbol:
      /* A symbol: process the value of the symbol recursively
	 as if it appeared here directly.  Avoid error if symbol void.
	 Special case: if value of symbol is a string, output the string
	 literally.  */
      {
	register Lisp_Object tem;
	tem = Fboundp (elt);
	if (!NULL (tem))
	  {
	    tem = Fsymbol_value (elt);
	    /* If value is a string, output that string literally:
	       don't check for % within it.  */
	    if (XTYPE (tem) == Lisp_String)
	      {
		layout_string_inc(w,XSTRING(tem)->data,
				  hpos,
				  0,minendcol,maxendcol,
				  l,mode_style,
				  &hpos,1);
	      }
	    else if (XTYPE(tem) == Lisp_Style)
	      {
		mode_style = XSTYLE(tem);
	      }
	    /* Give up right away for nil or t.  */
	    else if (!EQ (tem, elt))
	      { elt = tem; goto tail_recurse; }
	  }
      }
      break;
    case Lisp_Cons:
      {
	register Lisp_Object car, tem;
	/* A cons cell: three distinct cases.
	 * If first element is a string or a cons, process all the elements
	 * and effectively concatenate them.
	 * If first element is a negative number, truncate displaying cdr to
	 * at most that many characters.  If positive, pad (with spaces)
	 * to at least that many characters.
	 * If first element is a symbol, process the cadr or caddr recursively
	 * according to whether the symbol's value is non-nil or nil.
	 */
	car = XCONS (elt)->car;
	if (XTYPE (car) == Lisp_Symbol)
	  {
	    tem = Fboundp (car);
	    elt = XCONS (elt)->cdr;
	    if (XTYPE (elt) != Lisp_Cons)
	      goto invalid;
	    /* elt is now the cdr, and we know it is a cons cell.
	       Use its car if CAR has a non-nil value.  */
	    if (!NULL (tem))
	      {
		tem = Fsymbol_value (car);
		if (!NULL (tem))
		  { elt = XCONS (elt)->car; goto tail_recurse; }
	      }
	    /* Symbol's value is nil (or symbol is unbound)
	     * Get the cddr of the original list
	     * and if possible find the caddr and use that.
	     */
	    elt = XCONS (elt)->cdr;
	    if (NULL (elt))
	      break;
	    else if (XTYPE (elt) != Lisp_Cons)
	      goto invalid;
	    elt = XCONS (elt)->car;
	    goto tail_recurse;
	  }
	else if (XTYPE (car) == Lisp_Int)
	  {
	    register int lim = XINT (car);
	    elt = XCONS (elt)->cdr;
	    if (lim < 0)
	      /* Negative int means reduce maximum width.
	       * DO NOT change MINENDCOL here!
	       * (20 -10 . foo) should truncate foo to 10 col
	       * and then pad to 20.
	       */
	      maxendcol = min (maxendcol, hpos - lim);
	    else if (lim > 0)
	      {
		/* Padding specified.  Don't let it be more than
		 * current maximum.
		 */
		lim += hpos;
		if (lim > maxendcol)
		  lim = maxendcol;
		/* If that's more padding than already wanted, queue it.
		 * But don't reduce padding already specified even if
		 * that is beyond the current truncation point.
		 */
		if (lim > minendcol)
		  minendcol = lim;
	      }
	    goto tail_recurse;
	  }
	else if (XTYPE (car) == Lisp_String || XTYPE (car) == Lisp_Cons)
	  {
	    register int limit = 50;
	    /* LIMIT is to protect against circular lists.  */
	    while (XTYPE (elt) == Lisp_Cons && --limit > 0
		   && hpos < maxendcol)
	      {
		hpos = layout_mode_element(w,hpos,depth,
					   hpos,
					   maxendcol,
					   XCONS(elt)->car,l);
		elt = XCONS (elt)->cdr;		
	      }
	  }
	else if (XTYPE(car) == Lisp_Style)
	  {
	    mode_style = XSTYLE(car);
	    elt = XCONS(elt)->cdr;
	    goto tail_recurse;
	  }
      }
      break;
      
    default:
    invalid:
      layout_string_inc(w,"*invalid*",hpos,0,
			minendcol,maxendcol,
			l,mode_style,&hpos,1);
    }
 end:
  if (minendcol > hpos)
    {
      layout_string_inc(w,"",hpos,0,minendcol,-1,
			l,mode_style,&hpos,0);
    }
  return hpos;
}
#undef ADD_CHAR
#undef NEXT_CHAR
     
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return a string for the output of a mode line %-spec
   for window W, generated by character C and width MAXWIDTH.  */
char mode_buf[300];
char *
decode_mode_spec (w, c, maxwidth)
     struct window *w;
     register char c;
     register int maxwidth;
{
  Lisp_Object obj = Qnil;
  char *decode_mode_spec_buf = mode_buf;

  switch (c)
    {
    case 'b': 
      obj = current_buffer->name;
      break;

    case 'f': 
      obj = current_buffer->filename;
      break;

    case 'm': 
      obj = current_buffer->mode_name;
      break;

    case 'n':
      if (BEGV > BEG || ZV < Z)
	return " Narrow";
      break;

    case '*':
      if (!NULL (current_buffer->read_only))
	return "%";
      if (MODIFF > current_buffer->save_modified)
	return "*";
      return "-";

    case 's':
      /* status of process */
#ifdef subprocesses
      obj = Fget_buffer_process (Fcurrent_buffer ());
      if (NULL (obj))
	return "no process";
      obj = Fsymbol_name (Fprocess_status (obj));
      break;
#else
      return "no processes";
#endif /* subprocesses */

    case 'p':
      {
	int pos = marker_position (w->start);
	int total = ZV - BEGV;

	if (XFASTINT (w->window_end_pos) <= Z - ZV)
	  {
	    if (pos <= BEGV)
	      return "All";
	    else
	      return "Bottom";
	  }
	else if (pos <= BEGV)
	  return "Top";
	else
	  {
	    total = ((pos - BEGV) * 100 + total - 1) / total;
	    /* We can't normally display a 3-digit number,
	       so get us a 2-digit number that is close.  */
	    if (total == 100)
	      total = 99;
	    sprintf (decode_mode_spec_buf, "%2d%%", total);
	    return decode_mode_spec_buf;
	  }
      }

    case '[': 
      {
	int i;
	char *p;
	if (command_loop_level > 5)
	  return "[[[... ";
	p = decode_mode_spec_buf;
	for (i = 0; i < command_loop_level; i++)
	  *p++ = '[';
	*p = 0;
	return decode_mode_spec_buf;
      }

    case '%':
      return "%";

    case ']': 
      {
	int i;
	char *p;
	if (command_loop_level > 5)
	  return " ...]]]";
	p = decode_mode_spec_buf;
	for (i = 0; i < command_loop_level; i++)
	  *p++ = ']';
	*p = 0;
	return decode_mode_spec_buf;
      }

    case '-':
      {
	int i;
	if (maxwidth < 140)
	  return "--------------------------------------------------------------------------------------------------------------------------------------------";
	for (i = 0; i < maxwidth; i++)
	  decode_mode_spec_buf[i] = '-';
	return decode_mode_spec_buf;
      }
    }

  if (XTYPE (obj) == Lisp_String)
    return (char *) XSTRING (obj)->data;
  else
    return "";
}

/*
 * Debugging functions
 *
 */
print_window(w)
     struct window *w;
{
  struct line_header *l;
  int i;
  struct char_block *cb;

  fprintf(stderr,"Window is:\n");
  l = XLINE(w->lines);
  while (l)
    {
      cb = l->body;
      while (cb)
	{
	  fprintf(stderr,"%c",cb->ch);
	  cb= cb->next;
	}
      fprintf(stderr,"\n\r");
      l = l->next;
    }

  l = XLINE(w->modeline);
  cb = l->body;
  while (cb->ch != 0)
    {
      fprintf(stderr,"%c",cb->ch);
      cb = cb->next;
    }
  fprintf(stderr,"\n");
}

syms_of_xdisp ()
{
  staticpro (&last_arrow_position);
  staticpro (&last_arrow_string);
  last_arrow_position = Qnil;
  last_arrow_string = Qnil;

  DEFVAR_LISP ("global-mode-string", &Vglobal_mode_string,
    "String displayed by mode-line-format's \"%m\" specifiation.");
  Vglobal_mode_string = Qnil;

  DEFVAR_LISP ("overlay-arrow-position", &Voverlay_arrow_position,
    "Marker for where to display an arrow on top of the buffer text.\n\
This must be the beginning of a line in order to work.\n\
See also overlay-arrow-string.");
  Voverlay_arrow_position = Qnil;

  DEFVAR_LISP ("overlay-arrow-string", &Voverlay_arrow_string,
    "String to display as an arrow.  See also overlay-arrow-position.");
  Voverlay_arrow_string = Qnil;

  DEFVAR_INT ("scroll-step", &scroll_step,
    "*The number of lines to try scrolling a window by when point moves out.\n\
If that fails to bring point back on screen, point is centered instead.\n\
If this is zero, point is always centered after it moves off screen.");

  DEFVAR_BOOL ("reset-terminal-on-clear", &reset_terminal_on_clear,
    "Non-nil means re-init terminal modes for clear screen as on entry to Emacs.");
  reset_terminal_on_clear = 1;

  DEFVAR_BOOL ("truncate-partial-width-windows",
	       &truncate_partial_width_windows,
    "*Non-nil means truncate lines in all windows less than full screen wide.");
  truncate_partial_width_windows = 1;

  DEFVAR_BOOL ("mode-line-inverse-video", &mode_line_inverse_video,
    "*Non-nil means use inverse video, or other suitable display mode, for the mode line.");
  mode_line_inverse_video = 1;

  defsubr (&Sredraw_display);
  defsubr (&Sset_screen_modified);
  defsubr (&Sredisplay_screen);
}

/* initialize the window system */
init_xdisp ()
{
  Lisp_Object root_window;
#ifndef COMPILER_REGISTER_BUG
  register
#endif COMPILER_REGISTER_BUG
    struct window *mini_w;

  mini_w = XWINDOW (minibuf_window);
  root_window = mini_w->prev;

  echo_area_contents = 0;
  prev_echo_area_contents = 0;

  this_line_bufpos = 0;

}
