/*
 * Epoch 4.0 display code - Abstract redisplay code.
 *
 * $Revision: 1.58 $
 * $Source: /import/kaplan/stable/distrib/epoch-4.2/src/RCS/dispepoch.c,v $
 * $Date: 92/08/26 23:14:04 $
 * $Author: marca $
 */

/* #define CURSOR_DEBUG */

#ifndef LINT
static char rcsid[] = "$Author: marca $ $Date: 92/08/26 23:14:04 $ $Source: /import/kaplan/stable/distrib/epoch-4.2/src/RCS/dispepoch.c,v $ $Revision: 1.58 $";
#endif

#include <signal.h>

#include "config.h"
#include <stdio.h>
#include <errno.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
#include <fcntl.h>
#endif /* TCOUTQ defined */
#else
#ifndef VMS
#include <sys/ioctl.h>
#endif /* not VMS */
#endif /* not HAVE_TERMIO */

/* Allow m- file to inhibit use of FIONREAD.  */
#ifdef BROKEN_FIONREAD
#undef FIONREAD
#endif

/* We are unable to use interrupts if FIONREAD is not available,
   so flush SIGIO so we won't try. */
#ifndef FIONREAD
#ifdef SIGIO
#undef SIGIO
#endif
#endif

#undef NULL

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

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

#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 into stdio, but it isn't.
 * Some s- files overide this */
#ifdef __GNU_LIBRARY__
#define	PENDING_OUTPUT_COUNT(FILE) ((FILE)->__bufp - (FILE)->__buffer)
#else
#define PENDING_OUTPUT_COUNT(FILE) ((FILE)->_ptr - (FILE)->_base)
#endif
#endif /* No PENDING_OUTPUT_COUNT */

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

int screen_garbaged;

/* Nonzero means last display completed and cursor is really at
   WS->cursor_x, WS->cursor_y.  Zero means it was preempted. */

int display_completed;

int inverse_video;

int visible_bell;

int baud_rate;

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

Lisp_Object Vwindow_system;

/* 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;

/* Stdio stream being used for copy of all terminal output.  */

FILE *termscript;

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

extern int distinct_minibuffer;
extern int screen_changed;

extern short text_width();

extern struct Root_Block *cur_root;
extern struct Root_Block *mini_root;
extern struct Root_Block *root;

extern struct redisplay_block display_list[300];
extern int list_ptr;

struct char_block old_cur_char;
struct line_header old_cur_line;

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int
scroll_screen_lines(from,end,amount,w)
     int from,end,amount;
     struct window *w;
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  
  register struct line_header *l,*l1,*l2;
  register int i;

  if (amount == 0) return 1;

  list_ptr++;
#if 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;
#endif

  /* Find start and end of region */
  l = XLINE(w->lines);

  i = from;
  while (l && i--) l = l->next;
  l1 = l2 = l;			/* start */

  /* If we're confused, signal to do a regular layout */
  if (!l)
    {
      list_ptr--;
      return -1;
    }

  /* Find end block of region; this isn't entirely accurate, and may
   * be clipped.
   */
  while (l->next && i--) l = l->next;
  l2 = l;			/* end */

  BLOCK_TYPE(list_ptr) = BLIT;
  BLOCK_BLIT_OLD_TOP(list_ptr) = l1->ypos;
  l1->prevy = l1->ypos;
  BLOCK_BLIT_START(list_ptr) = l1;
  BLOCK_BLIT_END(list_ptr) = l2;

  if (amount > 0)
    {
      struct line_header *l3;

      for (i = 0; i < amount; i++)
	{
	  l = get_line();

	  if (l1->prev)
	    {
	      /* Inserting line in middle of window */
	      l->prev = l1->prev;
	      l1->prev->next = l;
	    }
	  else
	    {
	      /* New first line in window */
	      XSET(w->lines,Lisp_Raw_Data,l);
	    }
	  
	  l->next = l1;
	  l1->prev = l;
	}
      /* At this point, ypos in block to be shifted is wrong, but can't
       * be corrected til these new lines are layed out.
       */
    }
  if (amount < 0)
    {
      for (i = 0; i < (-amount); i++)
	{
	  l = l1->prev;

	  l1->prev = l->prev;	  
	  if (l->prev)
	    {
	      l->prev->next = l1;
	    }
	  if (l == XLINE(w->lines))	  
	    XSET(w->lines,Lisp_Raw_Data,l1);      
	  free_line(l);
	}
      
      i = l1->prev ? (l1->prev->ypos + l1->prev->descent) : w->pixtop;

      while (l1)
	{
	  l1->prevy = l1->ypos;
	  l1->ypos = i + l1->ascent;

	  i += (l1->ascent + l1->descent);

	  l1 = l1->next;
	}
    }
  return 1;
}
     

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Replot an Emacs window in its screen.  This will also fly for minibuffer
 * window.
 */
#ifdef DEFINE_CHANGE_FUNCTIONS
static int cur_buf_modif;
#endif

void
update_window(w)
     struct window *w;
{
  struct Root_Block *rb = XROOT(w->root);
  WS_DEF;
  struct line_header *l,*start,*end;
  struct char_block *cb,*endc;
  int i = 0;
  extern int DEBUG_ALL;

  /* Running under X.  We want to find any lines which have been
   * marked "shifted"
   */
#if 0  
  if (DEBUG_ALL && w == XWINDOW(selected_window))
    fprintf(stderr,"Updating %d\n",list_ptr);
#endif  
  if (list_ptr > -1)
    {
      if (BLOCK_TYPE(0) != AREA)
	CXTupdate_begin(w);

      for (i = 0; i <= list_ptr; i++)
	if (BLOCK_TYPE(i) == LINE &&
	    BLOCK_LINE(i)->shifted && !BLOCK_LINE(i)->changed) break;
      if (i < list_ptr)
	{
	  /* Found start, now find end of region */
	  start = BLOCK_LINE(i);
	  BLOCK_DONE(i) = 1;	/* Mark line as done */
	  for (; i <= list_ptr; i++)
	    {
	      if (BLOCK_TYPE(i) != LINE ||
		  BLOCK_LINE(i)->changed)
		break;
	      end = BLOCK_LINE(i);
	      BLOCK_DONE(i) = 1; /* Mark line as done */
	    }
	  shift_region(w,start,end);
	}
  
      i = 0;

      while (i <= list_ptr)
	{
	  if (!BLOCK_DONE(i))
	    switch (BLOCK_TYPE(i))
	      {
	      case LINE:	/* A regular line */
		l = BLOCK_LINE(i);
		cb = BLOCK_LINE_START(i);
		endc = BLOCK_LINE_END(i);
		update_line(w,l,cb,endc,BLOCK_LINE_CLEAR(i));
		l->new = l->changed = l->shifted = 0; /* Line is up to date */
		break;
	      case AREA:	/* Region to blank */
		clear_window_end(w,BLOCK_AREA_TOP(i),BLOCK_AREA_BOTTOM(i));
		break;
	      case BLIT:	/* Region to copy */
		shift_region(w,BLOCK_BLIT_START(i),BLOCK_BLIT_END(i));
		break;
	      default:
		break;
	      }
	  i++;
	}
      list_ptr = -1;

      if (BLOCK_TYPE(0) != AREA)
	CXTupdate_end(w);
    }

  if (rb == cur_root && w == XWINDOW(rb->select))
    {
#ifdef DEFINE_CHANGE_FUNCTIONS      
      if (w != XWINDOW(rb->minibuf_window) &&
	  (BUF_MODIFF(XBUFFER(w->buffer)) == cur_buf_modif) &&
	  (point != w->last_point))
	{
	  signal_after_movement((NIL(w->last_point)) ? point : w->last_point);
/*	  fprintf(stderr,"Foobar\n"); */
	}

      if (w != XWINDOW(rb->minibuf_window))
	cur_buf_modif = BUF_MODIFF(XBUFFER(w->buffer));
#endif      
      update_cursor(rb,(BLOCK_TYPE(0) == BLIT));
    }

  display_completed = 1;
  bzero(display_list,300*sizeof(struct redisplay_block));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Clean up at end of redisplay for screen.
 */
update_cursor(rb,blit)
     struct Root_Block *rb;
     int blit;
{
  WS_DEF;

  /* We haven't called update_end(), so InUpdate = 1, and move_cursor
   * will simply plot cursor in new position.
   */
  if (cursor_in_echo_area < 0)
    {
      /* Put cursor in */
      ws->new_cur_char = ws->new_cur_line->body;
      ws->cursor_x = 0;
      move_cursor(ws->new_cur_line,
		  ws->new_cur_char,
		  ws->cursor_y,
		  ws->cursor_x,
		  ws->new_cur_w,rb);
    }
  else if (cursor_in_echo_area)
    {
      /* Put cursor */
      ws->new_cur_char = ws->new_cur_line->end;
      ws->cursor_x = ws->new_cur_line->chars;
      move_cursor(ws->new_cur_line,
		  ws->new_cur_char,
		  ws->cursor_y,
		  ws->cursor_x,
		  ws->new_cur_w,rb);
    }
  else 
    {
      if (blit)
	{
	  ws->cur_char = ws->new_cur_char;
	  ws->cur_line = ws->new_cur_line;
	  ws->cur_w = ws->new_cur_w;
	  ws->vis_ypos = ws->cursor_y;
	  ws->vis_xpos = ws->cursor_x;

	  Fastmove_cursor(rb);
	}
      else
	move_cursor(ws->new_cur_line,
		    ws->new_cur_char,
		    ws->cursor_y,
		    ws->cursor_x,
		    ws->new_cur_w,rb);
    }
#ifdef CURSOR_DEBUG
  if (rb != mini_root)
    {
      fprintf(stderr,"Cursor at %d,%d (%c)\tPreviously at %d,%d (%c)\n",
	      ws->cursor_x,ws->cursor_y,ws->new_cur_char->ch,
	      ws->vis_xpos,ws->vis_ypos,ws->cur_char->ch);      
    }
#endif
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Handle forward and backward cursor movement on current line
 */
int 
output_forward_char(n)
     int n;
{
  register struct window *w = XWINDOW(selected_window);  
  struct Root_Block *rb = XROOT(w->root);  
  register struct W_Screen *ws = XWSCREEN(rb->win);

  /* Silently fail at window edges or on horizontally scrolled windows */
  if ((n < 0 && (ws->vis_xpos == 0 || ws->vis_xpos == ws->cur_line->chars)) ||
      (n > 0 && ws->vis_xpos >= ws->cur_line->chars - 1) ||
      w->hscroll)
    return 0;
  
  /* Set new cursor position.  Moving cursor will set this equal to the
   * prev position.
   */
  ws->cursor_x += n;
  ws->new_cur_line = ws->cur_line;
  ws->new_cur_char = (n > 0) ? ws->cur_char->next : ws->cur_char->prev;
  ws->new_cur_w = w;

#ifdef DEFINE_CHANGE_FUNCTIONS
  if (w != XWINDOW(rb->minibuf_window))
    {
      signal_after_movement((NIL(w->last_point)) ? point : w->last_point);
/*      fprintf(stderr,"Foobar\n"); */
    }
#endif  
  
  XFASTINT(w->last_point_x) = ws->cursor_x;
  XFASTINT(w->last_point) = point;  

  move_cursor(ws->new_cur_line,ws->new_cur_char,
	      ws->cursor_y,ws->cursor_x,ws->new_cur_w,rb);
  
  return 1;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Handle insertion of a character at 'cur_line,cur_char'
 * Assume that redisplay() is atomic for each screen and won't be preempted.
 * Do fast insertion of single char on current line, assuming display
 * changes will be bounded by current line.  If insertion is in middle of
 * line, utilize bit blt'ing under X/terminal insert-char operation.
 */
int
output_for_insert(c)
     int c;
{
  register struct window *w = XWINDOW(selected_window);
  struct Root_Block *rb = XROOT(w->root);
  register struct W_Screen *ws = XWSCREEN(rb->win);
  register struct line_header *l = ws->cur_line;
  register struct char_block *new,*cb = ws->cur_char;
  register struct buffer *b = current_buffer;
  char a[2];			/* CRUDE */
  int pixright;
  struct X_font *font;
  extern Lisp_Object Voverlay_arrow_position;
  extern Lisp_Object Voverlay_arrow_string;
  
  if (
      /* Selected window is minibuffer & msg is displayed */
      (EQ(selected_window,minibuf_window) && echo_area_contents) ||
      /* Window is horizontally scrolled */
      (w->hscroll > 0) ||
      /* Cursor on first character on line */
      (cb == l->body) ||
      /* Invalid cursor position info */
      (ws->cursor_y < 0) ||
      (ws->cursor_y > w->height) ||
      !display_completed ||
      /* Buffer is shared */
      buffer_shared > 1 ||
      /* Line contains tabs */
      l->tabs ||
      /* Overlay arrow string is in this buffer */
      (XTYPE(Voverlay_arrow_string) == Lisp_String &&
       XTYPE(Voverlay_arrow_position) == Lisp_Marker &&
       XMARKER(Voverlay_arrow_position)->buffer == b)
      )
    return 0;			/* Silently fail in these cases */

  a[1]=0;
  new = get_block();
  new->ch = a[0] = c;
  if (cb->style == cb->prev->style)
    new->style = cb->style;
  else
    {
      Lisp_Object ob;
      ob = get_button(b,point,0,0,&w->start_button,0);
      if (!NULL(ob))
	new->style = XSTYLE(ob);
    }

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

  new->new = 1;			/* Flag -> this is new */
  new->width = text_width(font,a,1);
  new->xpos = cb->xpos;
  
  a[0] = '\\';
  pixright = XFASTINT(w->pixleft) + XFASTINT(w->pixwidth) -
    text_width(font,a,1);
    
  if ((l->lwidth + new->width) >= pixright ||
      XFASCENT(font) > l->ascent ||
      XFDESCENT(font) > l->descent)
    {
      /* Fail here if we are going to go over the edge of this window, or
       * the new character's font is a different height than the line's
       * ascent or descent.
       */
      free_blocks(new,new);
      return 0;
    }
  
  /* Link it into the line */
  new->prev = cb->prev;
  new->prev->next = new;

  new->next = cb;
  cb->prev = new;

  /* Adjust attributes for rest of line */
  for ( ; cb; cb->xpos += new->width, cb = cb->next);

  /* Update the display */
  if (new->next == l->end)
    update_line(w,l,new,0,0);	/* Don't clear end of line */	
  else
    insert_chars(w,l,new,new->next,l->end,0); /* Faster */      

  l->lwidth += new->width;
  l->chars++;

  /* Update the cursor */
  ws->cursor_x += 1;
  XFASTINT(w->last_point_x) = ws->cursor_x;
  Fastmove_cursor(rb);

  return 1;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* For pixel based fill mode, return info about whether inserting the next
 * character will push end of current line past fill-pixel value.
 * Return 1 if insertion will succeed, 0 if other action should take place
 * first
 */
int
pixel_insert_ok(c,fill)
     register int c;
     register int fill;
{
  register struct line_header *l = cur_Wscreen->cur_line;
  register struct char_block *cb = cur_Wscreen->cur_char;
  register struct buffer *b = current_buffer;
  register struct Lisp_Style *s;
  register struct X_font *font;
  
  char a[2];

  if (l->lwidth == 0) return 1;	/* Empty line */

  if (cb && (!cb->prev || cb->prev->style == cb->style))
    s = cb->style;
  else
    {
      Lisp_Object ob = get_button(b,point,0,0,
				  &XWINDOW(selected_window)->start_button,
				  0);
      if (!NULL(ob))
	s = XSTYLE(ob);
    }
  font = XXFONT(STYLE_FIELD(XWINDOW(selected_window),font,stylenorm));    
  if (s && (XTYPE(s->font) == Lisp_Raw_Data))
    font = XXFONT(s->font);  
  a[1] = c; a[0] = 0;

  return ((l->lwidth + text_width (font,a,1)) <= fill) ? 1 : 0;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Change screen height and/or width.  Note that new height and width are
 * now in pixels.  ASCII behaviour should be unchanged.
 */
change_screen_size(height,width,pretend,rb)
     register int height,width,pretend;
     struct Root_Block *rb;
{
  WS_DEF;
  extern int gc_in_progress;
  
  if (in_display || gc_in_progress)
    {
#if 0      
      fprintf(stderr,"Holding a resize for %d\n",rb->seq);
#endif
      
      ws->size_change_pending = 1;
      ws->new_width = width;
      ws->new_height = height;
      return;
    }

  ws->size_change_pending  = 0;
  change_screen_size_1(height,width,pretend,rb);
}

change_screen_size_1(height,width,pretend,rb)
     register int height,width,pretend;
     struct Root_Block *rb;
{
  WS_DEF;
#if 0
  fprintf(stderr,"Resizing %d, %d\n",rb->seq,pretend);
#endif
  
  if ((height == 0 || height == ws->pixh) &&
      (width == 0 || width == ws->pixw))
    {
      ws->size_change = 0;	/* Blow off */
      return 1;
    }

  RW_FIXUP(rb);

  if (height && height != ws->pixh)
    {
      if (height > MScreenLength) height = MScreenLength;
      ws->height = height / XFHEIGHT(XXFONT(XSTYLE(rb->stylenorm)->font));
      /* Window shouldn't become less than 1 line high. */
      if (ws->height < 1) ws->height = 1;
      ws->pixh = height;
      if (distinct_minibuffer)
	set_window_height(rb->ewin,height,1);
      else
	{
	  /* Fudge around screen's minibuffer */
	  XWINDOW(rb->minibuf_window)->pixheight =
	    XFHEIGHT(XXFONT(XSTYLE(rb->stylenorm)->font));
	  height -= XWINDOW(rb->minibuf_window)->pixheight;
	  XWINDOW(rb->minibuf_window)->pixtop = ws->in_border + height;
	  set_window_height(rb->ewin,height,1);
	}
    }
  if (width && width != ws->pixw)
    {
      if (width > MScreenWidth) width = MScreenWidth;
      ws->pixw = width;
      set_window_width(rb->ewin,width,1);
      if (!distinct_minibuffer)
	set_window_width(rb->minibuf_window,width,1);
      ws->width = width / XFWIDTH(XXFONT(XSTYLE(rb->stylenorm)->font));
    }
  rb->needs_update = Qt;
  screen_changed++;
  ws->size_change = 0;
  if (pretend > -1) redisplay_preserve_echo_area();
    
  return 1;
}
	

#ifdef SIGWINCH
window_change_signal ()
{
  int width, height;
  extern int errno;
  int old_errno = errno;

  get_screen_size (&width, &height);
  /* Record the new size, but don't reallocate the data structures now.
     Let that be done later outside of the signal handler.  */
  in_display++;
  change_screen_size (height, width, 0,cur_root);
  in_display--;
  signal (SIGWINCH, window_change_signal);

  errno = old_errno;
}
#endif /* SIGWINCH */
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Initialization done when Epoch fork is started.
 */
char *terminal_type;

init_display ()
{
  extern char **xargv;
  
  Lisp_Object style;  
#ifdef HAVE_X_WINDOWS
  extern Lisp_Object Vxterm;
  Vxterm = Qnil;
#endif

  
  Vwindow_system = Qnil;
  meta_key = 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 = (char *) egetenv ("DISPLAY");

      /* Note KSH likes to provide an empty string as an envvar value.  */
      if ((XD_display_name && *XD_display_name) || (disp && *disp))
	{
	  x_term_init ();
	  Vxterm = Qt;
	  Vwindow_system = intern ("x");
	  Vwindow_system_version = make_number (11);

	  goto term_init_done;
	}
#endif /* HAVE_X_WINDOWS */
      ;
    }

  /* Punt and run ASCII */
  punt_and_run_emacs(xargv);

  term_init_done:
  ;
#ifdef SIGWINCH
#ifndef CANNOT_DUMP
  if (initialized)
#endif /* CANNOT_DUMP */
    if (inhibit_window_system)
      signal (SIGWINCH, window_change_signal);
#endif /* SIGWINCH */
}

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 ("baud-rate", Fbaud_rate, Sbaud_rate, 0, 0, 0,
  "Return the output baud rate of the terminal.")
  ()
{
  Lisp_Object temp;
  XSET (temp, Lisp_Int, baud_rate);
  return temp;
}

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 (noninteractive)  /* 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))
    {
      bell ();
      fflush (stdout);
    }
  else
    bell ();
  return Qnil;
}

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

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,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);

      /* In effect, timeout = end_time - timeout.
	 Break if result would be negative. */
      if (timeval_subtract (&timeout, end_time, timeout))
	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;
}

#ifdef HAVE_TIMEVAL

/* Subtract the `struct timeval' values X and Y,
   storing the result in RESULT.
   Return 1 if the difference is negative, otherwise 0.  */

int
timeval_subtract (result, x, y)
     struct timeval *result, x, y;
{
  /* Perform the carry for the later subtraction by updating y.
     This is safer because on some systems
     the tv_sec member is unsigned.  */
  if (x.tv_usec < y.tv_usec)
    {
      int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1;
      y.tv_usec -= 1000000 * nsec;
      y.tv_sec += nsec;
    }
  if (x.tv_usec - y.tv_usec > 1000000)
    {
      int nsec = (y.tv_usec - x.tv_usec) / 1000000;
      y.tv_usec += 1000000 * nsec;
      y.tv_sec -= nsec;
    }

  /* Compute the time remaining to wait.  tv_usec is certainly positive.  */
  result->tv_sec = x.tv_sec - y.tv_sec;
  result->tv_usec = x.tv_usec - y.tv_usec;
   /* Return indication of whether the result should be considered negative. */
  return x.tv_sec < y.tv_sec;
}
#endif /* HAVE_TIMEVAL */

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))
    redisplay_preserve_echo_area ();
  if (XINT (n) > 0)
    {
#ifdef subprocesses
#ifdef SIGIO
      gobble_input ();
#endif				/* SIGIO */
      {
	long start_time = time(0);
	long wait_time = (long) XINT(n);
	extern int kbd_count;

	do
	  {
	    wait_reading_process_input (wait_time, 1, 1,0);
	    detect_input_pending();
	    Fepoch_dispatch_events();
	    wait_time -= time(0) - start_time;
	  }
	while (wait_time > 0 && kbd_count == 0);
      }
#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;
}  



syms_of_display ()
{
  defsubr (&Sopen_termscript);
  defsubr (&Sding);
  defsubr (&Ssit_for);
  defsubr (&Ssleep_for);
  defsubr (&Sbaud_rate);
  defsubr (&Ssend_string_to_terminal);

  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.");
  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_version = Qnil;
      Vwindow_system = Qnil;
    }
}
