/* Newly written part of redisplay code.
   Copyright (C) 1985, 1986, 1987, 1988 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.  Refer to the GNU Emacs General Public
License for full details.

Everyone is granted permission to copy, modify and redistribute
GNU Emacs, but only under the conditions described in the
GNU Emacs General Public License.   A copy of this license is
supposed to have been given to you along with GNU Emacs so you
can know your rights and responsibilities.  It should be in a
file named COPYING.  Among other things, the copyright notice
and this notice must be preserved on all copies.  */


#include <signal.h>

#include "config.h"
#include <stdio.h>

#ifdef HAVE_TIMEVAL
#ifdef HPUX
#include <time.h>
#else
#include <sys/time.h>
#endif
#endif

#ifdef HAVE_TERMIO
#include <termio.h>
#ifdef TCOUTQ
#undef TIOCOUTQ
#define TIOCOUTQ TCOUTQ
#endif /* TCOUTQ defined */
#else
#ifndef VMS
#include <sys/ioctl.h>
#endif /* not VMS */
#endif /* not HAVE_TERMIO */

#undef NULL

#include "termchar.h"
#include "termopts.h"
#include "cm.h"
#include "dispextern.h"
#include "lisp.h"
#include "buffer.h"
#include "window.h"
#include "commands.h"

#include "screen.h"  /* [amc] */
#include "screenW.h" /* [amc] */

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

#ifndef PENDING_OUTPUT_COUNT
/* Get number of chars of output now in the buffer of a stdio stream.
   This ought to be built in in stdio, but it isn't.
   Some s- files override this because their stdio internals differ.  */
#define PENDING_OUTPUT_COUNT(FILE) ((FILE)->_ptr - (FILE)->_base)
#endif

/* Nonzero means do not assume anything about current
 contents of actual terminal screen */

int screen_garbaged;

/* Desired terminal cursor position (to show position of point),
 origin zero */

#ifndef CB
int cursX, cursY;
#endif

/* Nonzero means last display completed and cursor is really at cursX, cursY.
 Zero means it was preempted. */

int display_completed;

int visible_bell;	/* If true and the terminal will support it
			   then the screen will flash instead of
			   feeping when an error occurs */
int inverse_video;	/* If true and the terminal will support it
			   then we will use inverse video */

int baud_rate;		/* Terminal speed, so we can calculate
			   the number of characters required to
			   make the cursor sit still for n secs. */

Lisp_Object Vwindow_system;	/* nil or a symbol naming the window system
				   under which emacs is running
				   ('x is the only current possibility) */

/* Version number of window system, or nil if no window system.  */
Lisp_Object Vwindow_system_version;

/* Nonzero means reading single-character input with prompt
   so put cursor on minibuffer after the prompt.  */

int cursor_in_echo_area;

#ifndef CB
/* the current (physical) screen */
struct display_line *PhysScreen[MScreenLength + 1];

/* the desired (virtual) screen */
struct display_line *DesiredScreen[MScreenLength + 1];

/* temporary Copy of PhysScreen made in update_screen */
struct display_line *OPhysScreen[MScreenLength + 1];
#endif

/* Record here all the display line objects, for debugging.  */
static struct display_line *all_lines[2 * MScreenLength];

FILE *termscript;	/* Stdio stream being used for copy of all kbdinput.  */

struct cm Wcm;		/* Structure for info on cursor positioning */

extern short ospeed;	/* Output speed (from sg_ospeed) */

int in_display;		/* 1 if in redisplay: can't handle SIGWINCH now.  */

int delayed_size_change;  /* 1 means SIGWINCH happened when not safe.  */
int delayed_screen_height;  /* Remembered new screen height.  */
int delayed_screen_width;   /* Remembered new screen width.  */

/* Use these to chain together free lines */

#define LINE_NEXT(l) (*(struct display_line **) l)
#define SET_LINE_NEXT(l, next) (*((struct display_line **) l) = next)

/* Chain of free display_line structures, chained thru LINE_NEXT.  */

struct display_line *free_display_lines;

/* Number of lines now free.  */

int free_line_count;
int total_line_count;

/* Allocate as many display_line structures
   as we are ever supposed to need.
   Called at startup, and also if screen size is changed.  */

make_display_lines ()
{
  register int i;
  register struct display_line *p, *p1;

#ifdef CB
  free_display_lines = 0;
  free_line_count = 0;
  screen_garbaged = 1;
  total_line_count = 0;
  return;

#else /* not CB */

  /* First, free any that are already allocated */

  for (p = free_display_lines; p;)
    {
      p1 = p;
      p = LINE_NEXT (p);
      free (p1);
    }
  free_display_lines = 0;
  free_line_count = 0;

  screen_garbaged = 1;

  /* Now allocate as many as we can possibly validly need */

  for (i = - MScreenLength; i < MScreenLength; i++)
    {
#ifdef HAVE_X_WINDOWS
      p = (struct display_line *) malloc (sizeof (struct display_line));
/* the following is the original - note that it only allocates a structure
 * big enough to hold the *current width of the screen* !
 * so, we have to either use the above (which is maximally sized) or
 * re-allocate lines whenever the screen gets wider
 */
#else
      p = (struct display_line *) malloc (sizeof (struct display_line) + SCREEN_WIDTH - MScreenWidth);
#endif /* HAVE_X_WINDOWS */
      if (!p) abort ();
      SET_LINE_NEXT (p, free_display_lines);
      free_display_lines = p;
      all_lines[i + MScreenLength] = p;
    }
  free_line_count = 2 * MScreenLength;
#endif /* CB */
}

#ifdef CB
void more_display_lines()
    {
    int i;
    struct display_line *p,*block;
    char *msg = "Holy Vectors Batman, I can't get more lines!\n";

    /* first, allocate the whole block of display lines */
    block = (struct display_line *)
      malloc(MScreenLength * sizeof (struct display_line));
    if (!block)
      {
	write(2,msg,strlen(msg));
	abort ();
      }

    for (i = MScreenLength, p = block; i > 0; --i, ++p)
	{
	SET_LINE_NEXT (p, free_display_lines);
	free_display_lines = p;
	free_line_count += 1;
	total_line_count += 1;
	}
    }
#endif /* CB */

/* Get one of the previously malloc'd display_line structures
   from the free pool.  */
struct display_line *
new_display_line ()
{
  register struct display_line *p = free_display_lines;

  if (!p)
      {
      more_display_lines();	/* will abort if no memory */
      p = free_display_lines;
      }

  free_display_lines = LINE_NEXT (p);

#ifdef CB
  bzero (p, sizeof (struct display_line));
#else
  bzero (p, p->body - (char *) p);
#endif
  SET_LINE_NEXT (p, (struct display_line *)1);	/* Mark as in use.  */
  free_line_count--;
  return p;
}

/* Put a display_line back in the free pool.  */

void
return_display_line (p)
     struct display_line *p;
{
  if (!p)
    return;
  if ((int) LINE_NEXT (p) != 1)
    abort ();			/* Already free.  */
  SET_LINE_NEXT (p, free_display_lines);
  free_display_lines = p;
  free_line_count++;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* dump all the physical screen records back into the free line pool */
Cclear_screen_records (rb)
	struct Root_Block *rb;
{
  register int i;
  WS_DEF;
  struct display_line **p = ws->phys_line;

  for (i = 1; i <= MScreenLength; i++,++p)
      if (*p) return_display_line(*p);
  bzero(ws->phys_line, (MScreenLength+1)*sizeof(ws->phys_line[0]));
}
void
clear_screen_records() { Cclear_screen_records(cur_root); }

Cclear_desired_lines(rb)
	struct Root_Block *rb;
    {
    register int i;
    WS_DEF;
    struct display_line **p = ws->desired_line;
    
    for (i = 1; i <= MScreenLength; i++,++p)
	if (*p) return_display_line(*p);
    bzero(ws->desired_line, (MScreenLength+1)*sizeof(ws->desired_line[0]));
    }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return the hash code of display_line p.  */
line_hash_code (p)
     register struct display_line *p;
{
  register char *body, *end;
  register int h = 0;
  if (!p)
    return 0;
  /* Give all lighlighted lines the same hash code
     so as to encourage scrolling to leave them in place.  */
  if (p->highlighted)
    return -1;

  body = p->body;
  end = body + p->length;
  *end = 0;
  if (!must_write_spaces)
    {
      while (*body++ == ' ');
      body--;
      if (body == end)
	return 1;
      while (end[-1] == ' ') end--;
    }
  while (body != end)
    h = (h << 5) + h + *body++;
  if (h)
    return h;
  return 1;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Return number of characters in display_line p,
   except don't count leading and trailing spaces
   unless the terminal requires those to be explicitly output.  */

line_draw_cost (p)
     struct display_line *p;
{
  register char *body;
  register int i;

  if (!p)
    return 0;

  if (must_write_spaces)
    return p->length;

  body = p->body - 1;
  for (i = p->length; i > 0 && body[i - 1] == ' '; i--);

  i -= count_blanks (p->body);
  return max (i, 0);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* The functions on this page are the interface from xdisp.c to redisplay.
 They take cursor position arguments in origin 0.

 The only other interface into redisplay is through setting
 cursX and cursY (in xdisp.c) and setting screen_garbaged. */

/* cancel_line eliminates any request to display a line at position `vpos' */

Ccancel_line (vpos,rb)
     int vpos;
	struct Root_Block *rb;
{
  WS_DEF;
  return_display_line (ws->desired_line[vpos+1]);
  ws->desired_line[vpos+1] = 0;
}
void
cancel_line(vpos) { Ccancel_line(vpos,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Get a display_line for displaying on line `vpos'
 and set it up for outputting starting at `hpos' within it.  */

struct display_line *
Cget_display_line (vpos, hpos, rb)
     int vpos;
     register int hpos;
	struct Root_Block *rb;
{
  register struct display_line *line;
  register char *p;
#ifdef BUTTON
  register unsigned char *a;
#endif
  WS_DEF;

  if (vpos < 0) abort ();

  line = ws->desired_line[vpos+1];

  if (line && line->length > hpos)
      {
      print_all_windows();
      abort ();
      }
  if (!line)
    line = new_display_line ();

  if (hpos > line->length)
    {
      p = line->body + line->length;
#ifdef BUTTON
      a = line->attrib + line->length;
#endif
      hpos -= line->length;
      line->length += hpos;
      while (--hpos >= 0)
#ifdef BUTTON
	  { *a++ = 0; *p++ = ' '; }
#else
          *p++ = ' ';
#endif
    }

  ws->desired_line[vpos+1] = line;

  return line;
}

struct display_line *get_display_line(vpos, hpos) int vpos,hpos;
{ return Cget_display_line(vpos,hpos,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Scroll lines from vpos `from' up to but not including vpos `end'
 down by `amount' lines (`amount' may be negative).
 Returns nonzero if done, zero if terminal cannot scroll them. */

int
Cscroll_screen_lines (from, end, amount, rb)
     int from, end, amount;
	struct Root_Block *rb;
{
  register int i;
  WS_DEF;

  if (!line_ins_del_ok)
    return 0;

  if (amount == 0)
    return 1;
  if (amount > 0)
    {
      set_terminal_window (end + amount);
      if (!scroll_region_ok)
	ins_del_lines (end, -amount);
      ins_del_lines (from, amount);
      set_terminal_window (0);

      for (i = end + amount; i >= end + 1; i--)
	return_display_line (ws->phys_line[i]);
      for (i = end; i >= from + 1; i--)
	  ws->phys_line[i+amount] = ws->phys_line[i];
      for (i = from + amount; i >= from + 1; i--)
	  ws->phys_line[i] = 0;
    }
  if (amount < 0)
    {
      set_terminal_window (end);
      ins_del_lines (from + amount, amount);
      if (!scroll_region_ok)
	ins_del_lines (end + amount, -amount);
      set_terminal_window (0);

      for (i = from + amount + 1; i <= from; i++)
	  return_display_line(ws->phys_line[i]);
      for (i = from + 1; i <= end ; i++)
	  ws->phys_line[i+amount] = ws->phys_line[i];
      for (i = end + amount + 1; i <= end; i++)
	  ws->phys_line[i] = 0;
    }
  return 1;
}
int scroll_screen_lines(from, end, amount)
	int from,end,amount;
{ return Cscroll_screen_lines(from,end,amount,cur_root); }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* After updating a window w that isn't the full screen wide,
 copy all the columns that w does not occupy
 into the DesiredScreen lines from the PhysScreen lines
 so that update_screen will not change those columns.  */

void
preserve_other_columns (w)
     struct window *w;
{
  register int vpos;
  register struct display_line *l1, *l2;
  int start = XFASTINT (w->left);
  int end = XFASTINT (w->left) + XFASTINT (w->width);
  int bot = XFASTINT (w->top) + XFASTINT (w->height);
  struct W_Screen *ws = XWSCREEN(XROOT(w->root)->win);

  for (vpos = XFASTINT (w->top); vpos < bot; vpos++)
    {
      if ((l1 = ws->desired_line[vpos + 1])
	  && (l2 = ws->phys_line[vpos + 1]))
	{
	  if (start > 0)
	    {
	      bcopy (l2->body, l1->body, start);
#ifdef BUTTON
	      bcopy(l2->attrib, l1->attrib, start);
#endif
	      if (l1->length < start && l1->length < l2->length)
		l1->length = min (start, l2->length);
	    }
	  if (l2->length > end && l1->length < l2->length)
	    {
	      while (l1->length < end)
		l1->body[l1->length++] = ' ';
	      bcopy (l2->body + end, l1->body + end, l2->length - end);
#ifdef BUTTON
	      bcopy (l2->attrib + end, l1->attrib + end, l2->length - end);
#endif
	      l1->length = l2->length;
	    }
	}
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* On discovering that the redisplay for a window was no good,
 cancel the columns of that window,
 so that when the window is displayed over again
 get_display_line will not complain. */

void
cancel_my_columns (w)
     struct window *w;
{
  register int vpos;
  register struct display_line *l;
  register int start = XFASTINT (w->left);
  register int bot = XFASTINT (w->top) + XFASTINT (w->height);
  register struct W_Screen *ws = XWSCREEN(XROOT(w->root)->win);

  for (vpos = XFASTINT (w->top); vpos < bot; vpos++)
    {
      if ((l = ws->desired_line[vpos + 1])
	  && l->length >= start)
	l->length = start;
    }
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* These functions try to perform directly and immediately on the screen
   the necessary output for one change in the buffer.
   They may return 0 meaning nothing was done if anything is difficult,
   or 1 meaning the output was performed properly.
   They assume that the screen was up to date before the buffer
   change being displayed.  THey make various other assumptions too;
   see command_loop_1 where these are called.  */

int
direct_output_for_insert (c)
     int c;
{
  register struct display_line *p = WS->phys_line[WS->cursor_y + 1];
#ifndef COMPILER_REGISTER_BUG
  register
#endif /* COMPILER_REGISTER_BUG */
    struct window *w = XWINDOW (selected_window);
#ifndef COMPILER_REGISTER_BUG
  register
#endif /* COMPILER_REGISTER_BUG */
    int hpos = WS->cursor_x;

  /* Give up if about to continue line */
  if (hpos - XFASTINT (w->left) + 1 + 1 >= XFASTINT (w->width)

  /* Avoid losing if cursor is in invisible text off left margin */
      || XINT (w->hscroll) && hpos == XFASTINT (w->left)
    
  /* Give up if cursor outside window (in minibuf, probably) */
      || WS->cursor_y < XFASTINT (w->top)
      || WS->cursor_x >= XFASTINT (w->top) + XFASTINT (w->height)

  /* Give up if cursor not really at cursX, cursY */
      || !display_completed

  /* Give up if w is minibuffer and a message is being displayed there */
      || EQ (selected_window, minibuf_window) && minibuf_message

      /* if shared buffers, then it's going to fail too. */
      || buffer_shared > 1
      )
    return 0;

  p->body[hpos] = c;
#ifdef BUTTON
  p->attrib[hpos] =
    hpos > 0 ? p->attrib[hpos-1] : get_button(bf_cur,point,0,0,0);
#endif
  unchanged_modified = bf_modified;
  beg_unchanged = bf_s1;
  XFASTINT (w->last_point) = point;
  XFASTINT (w->last_point_x) = WS->cursor_x;
  XFASTINT (w->last_modified) = bf_modified;

  reassert_line_highlight (0, WS->cursor_y);
  write_chars (p->body + hpos, 1);
#ifndef CB
  fflush (stdout);
#endif
  ++(WS->cursor_x);
  p->length = max (p->length, WS->cursor_x);
  p->body[p->length] = 0;
  return 1;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int
direct_output_forward_char (n)
     int n;
{
  register struct window *w = XWINDOW (selected_window);

  /* AMC:  The following are lose conditions.  I think they've never been
   * found before because the checks in keyboard.c didn't even succeed.
   *
   * If the cursor is on the left edge or within 2 of the right edge, then
   * it's too dangerous to do this, because of wrap lines and scrolling,
   * and horizontally split windows.
   */
  if (WS->cursor_x <= XFASTINT(w->left)
      || (WS->cursor_x >= (XFASTINT(w->left) + XFASTINT(w->width) - 2)))
      return 0;                         /* fail */

  WS->cursor_x += n;
  XFASTINT (w->last_point_x) = WS->cursor_x;
  XFASTINT (w->last_point) = point;
  topos (WS->cursor_y,WS->cursor_x);
#ifndef CB
  fflush (stdout);
#endif
  return 1;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Update the actual terminal screen based on the data in DesiredScreen.
   Value is nonzero if redisplay stopped due to pending input.
   FORCE nonzero means do not stop for pending input.  */

/* At the time this function is called,
   no line is common to PhysScreen and DesiredScreen.
   That is true again when this function returns. */

/* changed by [amc] */
update_a_screen (force, inhibit_hairy_id, rb)
     int force;
     int inhibit_hairy_id;	/* [amc] I think "id" = "insert/delete" */
     struct Root_Block *rb;
{
    register struct display_line **p;
    register struct display_line *l, *lnew;
    register int i;
    int pause;
    extern input_pending;
    WS_DEF;

    if (force < 0) { pause = 1 ; goto do_pause; }

#ifndef CB
    bcopy (ws->phys_line, OPhysScreen, sizeof (ws->phys_line));
#endif

    detect_input_pending ();
    if (input_pending && !force)
      {
	pause = 1;
	goto do_pause;
      }

    CXTupdate_begin (rb);

    if (!line_ins_del_ok)
      inhibit_hairy_id = 1;

    /* Don't compute for i/d line if just want cursor motion. */
    for (p = ws->desired_line+ws->height
	 ; p != ws->desired_line && *p == 0
	 ; --p)
	;

    /* Try doing i/d line, if not yet inhibited.  */
    if (!inhibit_hairy_id && p != ws->desired_line)
      force |= Cscrolling (rb);

    /* Update the individual lines as needed.  Do bottom line first.  */

    l = ws->desired_line[ws->height];
    if (l && l != ws->phys_line[ws->height])
      Cupdate_line(ws->phys_line[ws->height], l, ws->height-1, rb);
    for (i = 1; i < ws->height && (force || !input_pending); i++)
      {
	l = ws->phys_line[i];
	lnew = ws->desired_line[i];
	if (lnew && lnew != l)
	  {
	    Cupdate_line (l, lnew, i - 1, rb);
	    detect_input_pending();	/* is this too often? */
	  }
      }
    pause = (i < ws->height) ? i : 0;

    /* Now just clean up termcap drivers and set cursor, etc.  */
    if (!pause)
      {
	if (cursor_in_echo_area < 0)
	  CXTtopos (ws->height - 1, 0, rb);
	else if (cursor_in_echo_area)
	  CXTtopos (ws->height - 1,
		 (ws->phys_line[ws->height] == 0 ? 0
		  : min (ws->width - 1,
			 ws->phys_line[ws->height]->length)),
		    rb);
	else
	  CXTtopos (ws->cursor_y, max (min (ws->cursor_x, ws->width - 1), 0),
		    rb);
      }

    CXTupdate_end (rb);

    if (termscript)
      fflush (termscript);

    /* Here if output is preempted because input is detected.  */
  do_pause:

    if (ws->height == 0) abort (); /* Some bug zeros some core */
    display_completed = !pause;
    /* Free any lines still in desired screen but not in phys screen */
    /* Free any lines that used to be in phys screen but are no longer */
    for (p = ws->phys_line+ws->height; p != ws->phys_line; p--)
      if (p[0]) p[0]->physical = 1;
    for (p = ws->desired_line+ws->height; p != ws->desired_line; p--)
      if ((l = *p) && (!l->physical))
	{
	  return_display_line (l);
	  l->physical = 1;
	}

#ifndef CB
    if (force >= 0)			/* otherwise array not set! */
	for (p = &OPhysScreen[ws->height]; p != OPhysScreen; p--)
	    {
	    if (l = *p)
		{
		if (!l->physical)
		    return_display_line (l);
		}
	    }
#endif

    for (p = ws->phys_line+ws->height; p != ws->phys_line; p--)
      if (p[0]) p[0]->physical = 0;

#ifndef CB
    bzero (OPhysScreen, MScreenLength * sizeof OPhysScreen[0]);
#endif
    bzero (ws->desired_line, MScreenLength * sizeof ws->desired_line[0]);
    return pause;
}

/* Called when about to quit, to check for doing so
   at an improper time.  */

void
quit_error_check ()
{
  if (cur_Wscreen && cur_Wscreen->desired_line[1] != 0)
    abort ();
  if (cur_Wscreen && cur_Wscreen->desired_line[cur_Wscreen->height] != 0)
    abort ();
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Decide what insert/delete line to do, and do it */
#ifdef CB
int Cscrolling (rb) struct Root_Block *rb;
{
  int top,bot,i,j;
  int top_count,bot_count,top_start,bot_start;
  struct display_line *first,*last,*this,**l;
  char * body;
  WS_DEF;
  extern Lisp_Object minibuf_rootblock;

  if (rb == XROOT(minibuf_rootblock)) return 0; /* don't do minibuffer */

  top = 0;
  bot = ws->height;		/* pre-dec, so skips mode line */
  first = last = 0;
  top_count = bot_count = 0;

  /* find the first place that desired_line is set on the top/bottom */
  while (top < bot && !(first && last))
    {
      if (!first)
	{
	  struct display_line *t = ws->desired_line[++top];
	  first = t && t->length > 0 ? t : 0;
	}
      if (!last)
	{
	  struct display_line *t = ws->desired_line[--bot];
	  last = t && t->length > 0 ? t : 0;
	}
    }

  /* Find a duplicate of the first desired line, and see how far the
   * match goes
   */
  if (first)
    {
      body = first->body;
      for ( i=top+1, l=ws->phys_line+i ; i < ws->height ; ++i, ++l )
	if (*l && !strcmp(body,(*l)->body))
	  {
	    for ( j = 1 ; i + j <= ws->height ; ++j )
	      {
		struct display_line *dl = ws->desired_line[top+j];
		if ( !dl || (l[j] && strcmp(l[j]->body,dl->body)))
		  break;		/* end of matched set */
	      }
	    if (j > top_count)
	      {
		top_count = j;
		top_start = i;
	      }
	    break;
	  }
    }

  /* now, try that on the bottom line */
  if (last)
    {
      body = last->body;
      for ( i=bot-1, l=ws->phys_line+i ; i >= 1 ; --i, --l )
	if (*l && !strcmp(body,(*l)->body))
	  {
	    for ( j = -1 ; i+j > 0 ; --j )
	      {
		struct display_line *dl = ws->desired_line[bot+j];
		if ( !dl || (l[j] && strcmp(l[j]->body,dl->body)))
		  break;	/* end of matched set */
	      }
	    if (j < bot_count)
	      {
		bot_count = j;
		bot_start = i;
	      }
	    break;
	  }
    }

  if (top_count && top_count >= -bot_count)
    ShiftScreenLines(top,top_start,top_count,rb);
  else if (bot_count && -bot_count > top_count)
    ShiftScreenLines(bot,bot_start,bot_count,rb);

  return 0;
}
#else
int Cscrolling (rb)
	struct Root_Block *rb;
{
  int unchanged_at_top, unchanged_at_bottom;
  int window_size;
  int changed_lines;
  WS_DEF;
  int *old_hash = (int *) alloca (ws->height * sizeof (int));
  int *new_hash = (int *) alloca (ws->height * sizeof (int));
  int *draw_cost = (int *) alloca (ws->height * sizeof (int));
  register int i;
  int free_at_end_vpos = ws->height;
  
  /* Compute hash codes of all the lines.
     Also calculate number of changed lines,
     number of unchanged lines at the beginning,
     and number of unchanged lines at the end.  */

  changed_lines = 0;
  unchanged_at_top = 0;
  unchanged_at_bottom = ws->height;
  for (i = 0; i < ws->height; i++)
    {
      old_hash[i] = line_hash_code (ws->phys_line[i + 1]);
      if (!ws->desired_line[i + 1])
	ws->desired_line[i + 1] = ws->phys_line[i + 1];
      if (ws->phys_line[i + 1] == ws->desired_line[i + 1])
	new_hash[i] = old_hash[i];
      else
	new_hash[i] = line_hash_code (ws->desired_line[i + 1]);
      if (old_hash[i] != new_hash[i])
	{
	  changed_lines++;
	  unchanged_at_bottom = ws->height - i - 1;
	}
      else if (i == unchanged_at_top)
	unchanged_at_top++;
      draw_cost[i] = line_draw_cost (ws->desired_line[i + 1]);
    }

  /* If changed lines are few, don't allow preemption, don't scroll.  */
  if (changed_lines < baud_rate / 2400 || unchanged_at_bottom == ws->height)
    return 1;

  window_size = ws->height - unchanged_at_top - unchanged_at_bottom;

  if (scroll_region_ok)
    free_at_end_vpos -= unchanged_at_bottom;
  else if (memory_below_screen)
    free_at_end_vpos = -1;

#ifndef CB
  /* If large window, fast terminal and few lines in common between
     PhysScreen and DesiredScreen, don't bother with i/d calc.  */
  if (window_size >= 18 && baud_rate > 2400
      && (window_size >=
	  10 * scrolling_max_lines_saved (unchanged_at_top,
					  ws->height - unchanged_at_bottom,
					  old_hash, new_hash, draw_cost)))
    return 0;

  Cscrolling_1 (window_size, unchanged_at_top, unchanged_at_bottom,
	       draw_cost + unchanged_at_top - 1,
	       old_hash + unchanged_at_top - 1,
	       new_hash + unchanged_at_top - 1,
	       free_at_end_vpos - unchanged_at_top, rb);
#endif

  return 0;
}
#endif
int scrolling() { return Cscrolling(cur_root); }

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
Cupdate_line (old, new, vpos, rb)
     struct display_line *old, *new;
     int vpos;
	struct Root_Block *rb;
{
#ifdef BUTTON
  register char *obody, *nbody;
  register unsigned char *oattr, *nattr;
  register int i,length;
#else
  register char *obody, *nbody, *op1, *op2, *np1;
  int tem;
  int osp, nsp, m1, m2, olen, nlen;
  int save;
#endif
  WS_DEF;

  if (old == new)
    return;

  /* Mark physical screen as containing the line `new' */
  if (ws->phys_line[vpos + 1]) return_display_line(ws->phys_line[vpos+1]);
  ws->phys_line[vpos + 1] = new;

#ifdef BUTTON
  CXTtopos(vpos,0,rb);		/* start of line */

  if (!new)			/* line removed, clear it and finish */
      {
      CXTclear_end_of_line(old->length,rb);
      return;
      }
  nbody = new->body; nattr = new->attrib;

  if (!old)
      {
      UpdateTextLine(new,vpos,0,rb); /* just do the whole thing */
      return;
      }

  obody = old->body; oattr = old->attrib;

  length = new->length < old->length ? new->length : old->length;

  /* step through the lines until we find a non-match, and update from
   * there on
   */
  for ( i=0 ; i < length && nbody[i]==obody[i] && nattr[i]==oattr[i] ; ++i )
      ;
  UpdateTextLine(new,vpos,i,rb);
  return;
  }
#else  /* BUTTON */

  if ((new && new->highlighted) != (old && old->highlighted))
    {
      CXTchange_line_highlight (new && new->highlighted, vpos,
			      old ? old->length : 0, rb);
      old = 0;
    }
  else
    CXTreassert_line_highlight (new && new->highlighted, vpos, rb);

  if (!old)
    {
      olen = 0;
    }
  else
    {
      obody = old -> body;
      olen = old->length;
      if (! old->highlighted)
	{
	  /* Note obody[-1] is old->physical, which is always 0 or 1.  */
	  if (!must_write_spaces)
	    while (obody[olen - 1] == ' ')
	      olen--;
	}
      else
	{
	  /* For an inverse-video line, remember we gave it
	     spaces all the way to the screen edge
	     so that the reverse video extends all the way across.  */
	  while (olen < ws->width - 1)
	    obody[olen++] = ' ';
	}
    }

  if (!new)
    {
      nlen = 0;
      goto just_erase;
    }

  nbody = new -> body;
  nlen = new->length;

  /* Pretend trailing spaces are not there at all,
     unless for one reason or another we must write all spaces.  */
  /* We know that the previous character is the `physical' field
     and it is zero or one.  */
  if (! new->highlighted)
    {
      if (!must_write_spaces)
	while (nbody[nlen - 1] == ' ')
	  nlen--;
    }
  else
    {
      /* For an inverse-video line, give it extra trailing spaces
	 all the way to the screen edge
	 so that the reverse video extends all the way across.  */
      while (nlen < ws->width - 1)
	nbody[nlen++] = ' ';
    }

  /* If there's no i/d char, quickly do the best we can without it.  */
  if (!char_ins_del_ok)
    {
      int i,j;

      for (i = 0; i < nlen; i++)
	{
	  if (i >= olen || nbody[i] != obody[i])
	    {
	      /* We found a non-matching char.  */
	      CXTtopos (vpos, i, rb);
	      for (j = 1; (i + j < nlen &&
			   (i + j >= olen || nbody[i+j] != obody[i+j]));
		   j++);
	      /* Output this run of non-matching chars.  */ 
	      CXTwrite_chars (nbody + i, j, rb);
	      i += j - 1;
	      /* Now find the next non-match.  */
	    }
	}
      /* Clear the rest of the line, or the non-clear part of it.  */
      if (olen > nlen)
	{
	  CXTtopos (vpos, nlen, rb);
	  CXTclear_end_of_line (olen, rb);
	}
      return;
    }

  if (!olen)
    {
      nsp = (must_write_spaces || new->highlighted)
	      ? 0 : count_blanks (nbody);
      if (nlen > nsp)
	{
	  CXTtopos (vpos, nsp, rb);
	  CXTwrite_chars (nbody + nsp, nlen - nsp, rb);
	}
      return;
    }

  obody[olen] = 1;
  save = nbody[nlen];
  nbody[nlen] = 0;

  /* Compute number of leading blanks in old and new contents.  */
  osp = count_blanks (obody);
  if (!new->highlighted)
    nsp = count_blanks (nbody);
  else
    nsp = 0;

  /* Compute number of matching chars starting with first nonblank.  */
  m1 = count_match (obody + osp, nbody + nsp);

  /* Spaces in new match implicit space past the end of old.  */
  /* A bug causing this to be a no-op was fixed in 18.29.  */
  if (!must_write_spaces && osp + m1 == olen)
    {
      np1 = nbody + nsp;
      while (np1[m1] == ' ')
	m1++;
    }

  /* Avoid doing insert/delete char
     just cause number of leading spaces differs
     when the following text does not match. */
  if (m1 == 0 && osp != nsp)
    osp = nsp = min (osp, nsp);

  /* Find matching characters at end of line */
  op1 = obody + olen;
  np1 = nbody + nlen;
  op2 = op1 + m1 - min (olen - osp, nlen - nsp);
  while (op1 > op2 && op1[-1] == np1[-1])
    {
      op1--;
      np1--;
    }
  m2 = obody + olen - op1;

  /* Put correct value back in nbody[nlen].
     This is important because direct_output_for_insert
     can write into the line at a later point.  */
  nbody[nlen] = save;

  /* tem gets the distance to insert or delete.
     m2 is how many characters we save by doing so.
     Is it worth it?  */

  tem = (nlen - nsp) - (olen - osp);
  if (m2 && tem && m2 <= DCICcost[tem])
    m2 = 0;

  /* nsp - osp is the distance to insert or delete.
     m1 + m2 is how much we save by doing so.
     Is it worth it?  */

  if (m1 + m2 && nsp != osp && m1 + m2 <= DCICcost[nsp - osp])
    {
      m1 = 0;
      m2 = 0;
      osp = nsp = min (osp, nsp);
    }

  /* Now go through the line, inserting, writing and deleting as appropriate.  */

  if (osp > nsp)
    {
      CXTtopos (vpos, nsp, rb);
      CXTdelete_chars (osp - nsp, rb);
    }
  else if (nsp > osp)
    {
      /* If going to delete chars later in line
	 and insert earlier in the line,
	 must delete first to avoid losing data in the insert */
      if (m2 && nlen < olen + nsp - osp)
	{
	  CXTtopos (vpos, nlen - m2 + osp - nsp, rb);
	  CXTdelete_chars (olen + nsp - osp - nlen, rb);
	  olen = nlen - (nsp - osp);
	}
      CXTtopos (vpos, osp, rb);
      insert_chars ((char *)0, nsp - osp);
    }
  olen += nsp - osp;

  tem = nsp + m1 + m2;
  if (nlen != tem || olen != tem)
    {
      CXTtopos (vpos, nsp + m1, rb);
      if (!m2 || nlen == olen)
	{
	  /* If new text being written reaches right margin,
	     there is no need to do clear-to-eol at the end.
	     (and it would not be safe, since cursor is not
	     going to be "at the margin" after the text is done) */
	  if (nlen == ws->width)
	    olen = 0;
	  CXTwrite_chars (nbody + nsp + m1, nlen - tem, rb);
	}
      else if (nlen > olen)
	{
	  CXTwrite_chars (nbody + nsp + m1, olen - tem, rb);
	  CXTinsert_chars (nbody + nsp + m1 + olen - tem, nlen - olen, rb);
	  olen = nlen;
	}
      else if (olen > nlen)
	{
	  CXTwrite_chars (nbody + nsp + m1, nlen - tem, rb);
	  CXTdelete_chars (olen - nlen, rb);
	  olen = nlen;
	}
    }

 just_erase:
  /* If any unerased characters remain after the new line, erase them.  */
  if (olen > nlen)
    {
      CXTtopos (vpos, nlen, rb);
      CXTclear_end_of_line (olen, rb);
    }
}
#endif /* BUTTON */

void
update_line (old, new, vpos)
     struct display_line *old, *new;
     int vpos;
{ Cupdate_line(old,new,vpos,cur_root); }
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
count_blanks (str)
     char *str;
{
  register char *p = str;
  while (*str++ == ' ');
  return str - p - 1;
}

count_match (str1, str2)
     char *str1, *str2;
{
  register char *p1 = str1;
  register char *p2 = str2;
  while (*p1++ == *p2++);
  return p1 - str1 - 1;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("open-termscript", Fopen_termscript, Sopen_termscript,
  1, 1, "FOpen termscript file: ",
  "Start writing all terminal output to FILE as well as the terminal.\n\
FILE = nil means just close any termscript file currently open.")
  (file)
     Lisp_Object file;
{
  if (termscript != 0) fclose (termscript);
  termscript = 0;

  if (! NULL (file))
    {
      file = Fexpand_file_name (file, Qnil);
      termscript = fopen (XSTRING (file)->data, "w");
      if (termscript == 0)
	report_file_error ("Opening termscript", Fcons (file, Qnil));
    }
  return Qnil;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("screen-height", Fscreen_height, Sscreen_height, 0, 0, 0,
  "Return number of lines on screen available for display.")
  ()
{
  return make_number (WS->height);
}

DEFUN ("screen-width", Fscreen_width, Sscreen_width, 0, 0, 0,
  "Return number of columns on screen available for display.")
  ()
{
  return make_number (WS->width);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Prevent window-change signals from being handled.  */
void
hold_window_change ()
{
  in_display = 1;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFSIMPLE ("baud-rate", Fbaud_rate, Sbaud_rate,
	   "Return the output baud rate of the terminal.",
	   Lisp_Int, XSETINT, baud_rate)

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
DEFUN ("send-string-to-terminal", Fsend_string_to_terminal,
  Ssend_string_to_terminal, 1, 1, 0,
  "Send STRING to the terminal without alteration.\n\
Control characters in STRING will have terminal-dependent effects.")
  (str)
     Lisp_Object str;
{
  CHECK_STRING (str, 0);
  fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, stdout);
  fflush (stdout);
  if (termscript)
    {
      fwrite (XSTRING (str)->data, 1, XSTRING (str)->size, termscript);
      fflush (termscript);
    }
  return Qnil;
}

void Ding ()
{
  if (noninteractive)
    putchar (07);
  else if (!INTERACTIVE)  /* Stop executing a keyboard macro. */
    error ("Keyboard macro terminated by a command ringing the bell");
  else
    ring_bell ();
  fflush (stdout);
}

DEFUN ("ding", Fding, Sding, 0, 1, 0,
  "Beep, or flash the screen.\n\
Terminates any keyboard macro currently executing unless an argument\n\
is given.")
  (arg)
  Lisp_Object arg;
{
  if (!NULL (arg))
    {
      ring_bell ();
      fflush (stdout);
    }
  else
    Ding ();
  return Qnil;
}

DEFUN ("sleep-for", Fsleep_for, Ssleep_for, 1, 1, 0,
  "Pause, without updating display, for ARG seconds.")
  (n)
     Lisp_Object n;
{
  register int t;
#ifndef subprocesses
#ifdef HAVE_TIMEVAL
  struct timeval timeout, end_time, garbage1;
#endif /* HAVE_TIMEVAL */
#endif /* no subprocesses */

  CHECK_NUMBER (n, 0);
  t = XINT (n);
  if (t <= 0)
    return Qnil;

#ifdef subprocesses
  wait_reading_process_input (t, 0, 0);
#else /* No subprocesses */
  immediate_quit = 1;
  QUIT;

#ifdef VMS
  sys_sleep (t);
#else /* not VMS */
/* The reason this is done this way 
    (rather than defined (H_S) && defined (H_T))
   is because the VMS preprocessor doesn't grok `defined' */
#ifdef HAVE_SELECT
#ifdef HAVE_TIMEVAL
  gettimeofday (&end_time, &garbage1);
  end_time.tv_sec += t;

  while (1)
    {
      gettimeofday (&timeout, &garbage1);
      timeout.tv_sec = end_time.tv_sec - timeout.tv_sec;
      timeout.tv_usec = end_time.tv_usec - timeout.tv_usec;
      if (timeout.tv_usec < 0)
	timeout.tv_usec += 1000000,
      timeout.tv_sec--;
      if (timeout.tv_sec < 0)
	break;
      if (!select (1, 0, 0, 0, &timeout))
	break;
    }
#else /* not HAVE_TIMEVAL */
  /* Is it safe to quit out of `sleep'?  I'm afraid to trust it.  */
  sleep (t);
#endif /* HAVE_TIMEVAL */
#else /* not HAVE_SELECT */
  sleep (t);
#endif /* HAVE_SELECT */
#endif /* not VMS */
  
  immediate_quit = 0;
#endif /* no subprocesses */
  return Qnil;
}

DEFUN ("sit-for", Fsit_for, Ssit_for, 1, 2, 0,
  "Perform redisplay, then wait for ARG seconds or until input is available.\n\
Optional second arg non-nil means don't redisplay.\n\
Redisplay is preempted as always if input arrives, and does not happen\n\
if input is available before it starts.\n\
Value is t if waited the full time with no input arriving.")
  (n, nodisp)
     Lisp_Object n, nodisp;
{
#ifndef subprocesses
#ifdef HAVE_TIMEVAL
  struct timeval timeout;
#else
  int timeout_sec;
#endif
  int waitchannels;
#endif /* no subprocesses */

  CHECK_NUMBER (n, 0);

  Fepoch_dispatch_events();	/* clear out event keys */

  if (detect_input_pending ())
    return Qnil;

  if (EQ (nodisp, Qnil))
    DoDsp (1);			/* Make the screen correct */
  if (XINT (n) > 0)
    {
#ifdef subprocesses
#ifdef SIGIO
      gobble_input ();
#endif				/* SIGIO */
      wait_reading_process_input (XINT (n), 1, 1);
#else				/* no subprocesses */
      immediate_quit = 1;
      QUIT;

      waitchannels = 1;
#ifdef VMS
      input_wait_timeout (XINT (n));
#else				/* not VMS */
#ifndef HAVE_TIMEVAL
      timeout_sec = XINT (n);
      select (1, &waitchannels, 0, 0, &timeout_sec);
#else				/* HAVE_TIMEVAL */
      timeout.tv_sec = XINT (n);  
      timeout.tv_usec = 0;
      select (1, &waitchannels, 0, 0, &timeout);
#endif				/* HAVE_TIMEVAL */
#endif				/* not VMS */

      immediate_quit = 0;
#endif				/* no subprocesses */
    }
  return detect_input_pending () ? Qnil : Qt;
}

DEFUN ("epoch::total-line-count",Fepoch_total_line_count,Sepoch_total_line_count,0,0,0,"Total number of display lines allocated") ()
{
  return make_number(total_line_count);
}

char *terminal_type;

/* Initialization done when Emacs fork is started, before doing stty. */
/* Determine terminal type and set terminal_driver */
/* Then invoke its decoding routine to set up variables
  in the terminal package */

init_display ()
{
#ifdef HAVE_X_WINDOWS
  extern Lisp_Object Vxterm;
  Vxterm = Qnil;
#endif

  Vwindow_system = Qnil;
  MetaFlag = 0;
  inverse_video = 0;
  cursor_in_echo_area = 0;
  terminal_type = (char *) 0;

  if (!inhibit_window_system)
    {
#ifdef HAVE_X_WINDOWS
      extern char *XD_display_name;
      char *disp = egetenv ("DISPLAY");

#ifndef CB
      /* Note KSH likes to provide an empty string as an envvar value.  */
      if (XD_display_name || (disp && *disp))
#endif
	{
	  x_term_init ();
	  Vxterm = Qt;
	  Vwindow_system = intern ("x");
#ifdef X11
	  Vwindow_system_version = make_number (11);
#else
	  Vwindow_system_version = make_number (10);
#endif
	  goto term_init_done;
	}
#endif /* HAVE_X_WINDOWS */
      ;
    }
#ifndef CB
  /* Look at the TERM variable */
  terminal_type = (char *) getenv ("TERM");
  if (!terminal_type)
    {
#ifdef VMS
      fprintf (stderr, "Please specify your terminal type.\n\
For types defined in VMS, use  set term /device=TYPE.\n\
For types not defined in VMS, use  define emacs_term \"TYPE\".\n\
\(The quotation marks are necessary since terminal types are lower case.)\n");
#else
      fprintf (stderr, "Please set the environment variable TERM; see tset(1).\n");
#endif
      exit (1);
    }
  term_init (terminal_type);

#endif /* CB */

 term_init_done:
  make_display_lines ();

}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
syms_of_display ()
{
  defsubr (&Sopen_termscript);
  defsubr (&Sding);
  defsubr (&Ssit_for);
  defsubr (&Sscreen_height);
  defsubr (&Sscreen_width);
#ifndef CB
  defsubr (&Sset_screen_height);
  defsubr (&Sset_screen_width);
#endif /* no CB */
  defsubr (&Ssleep_for);
  defsubr (&Sbaud_rate);
  defsubr (&Ssend_string_to_terminal);
  defsubr (&Sepoch_total_line_count);

  DEFVAR_BOOL ("inverse-video", &inverse_video,
    "*Non-nil means use inverse-video.");
  DEFVAR_BOOL ("visible-bell", &visible_bell,
    "*Non-nil means try to flash the screen to represent a bell.");
  DEFVAR_BOOL ("no-redraw-on-reenter", &no_redraw_on_reenter,
    "*Non-nil means no need to redraw entire screen after suspending.\n\
It is up to you to set this variable to inform Emacs.");
  DEFVAR_LISP ("window-system", &Vwindow_system,
    "A symbol naming the window-system under which Emacs is running,\n\
\(such as `x'), or nil if emacs is running on an ordinary terminal.");
  DEFVAR_LISP ("window-system-version", &Vwindow_system_version,
    "Version number of the window system Emacs is running under.");
  Vwindow_system_version = Qnil;
  DEFVAR_BOOL ("cursor-in-echo-area", &cursor_in_echo_area,
    "Non-nil means put cursor in minibuffer after any message displayed there.");

  /* Initialize `window-system', unless init_display already decided it.  */
#ifdef CANNOT_DUMP
  if (noninteractive)
#endif
    Vwindow_system = Qnil;
}
