/* Communication module for TTY terminals.
   Copyright (C) 1994, 1995 Board of Trustees, University of Illinois

This file is part of XEmacs.

XEmacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

XEmacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Synched up with: Divergent from FSF. */

/* This file has been Mule-ized, but needs more work before it will
   compile with Mule support enabled. */

/* Written by Chuck Thompson. */

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

#include "buffer.h"
#include "device.h"
#include "device-tty.h"
#include "events.h"
#include "faces.h"
#include "frame.h"
#include "glyphs.h"
#include "redisplay.h"
#include "sysdep.h"
#include "window.h"

/* These headers #define all kinds of common words like "columns"...
   What a bunch of losers.  If we were to include them, we'd have to
   include them last to prevent them from messing up our own header
   files (struct slot names, etc.).  But it turns out that there are
   other conflicts as well on some systems, so screw it: we'll just
   re-declare the routines we use and assume the code in this file is
   invoking them correctly. */
/* # include <curses.h> */
/* # include <term.h> */
extern int tgetent (CONST char *, CONST char *);
extern int tgetflag (CONST char *);
extern int tgetnum (CONST char *);
extern char *tgetstr (CONST char *, char **);
extern void tputs (CONST char *, int, void (*)(int));
char *tparam (CONST char *string, char *outstring, int len, int arg1, int arg2,
	      int arg3, int arg4, int arg5, int arg6, int arg7, int arg8,
	      int arg9);
#define OUTPUTN(d, a, n) \
  do { cmputc_device = d; tputs (a, n, cmputc); } while (0)
#define OUTPUT1(d, a) OUTPUTN (d, a, 1)
#define OUTPUTN_IF(d, a, n) \
  do { cmputc_device = d; if (a) tputs (a, n, cmputc); } while (0)
#define OUTPUT1_IF(d, a) OUTPUTN_IF (d, a, 1)

static void tty_output_string (struct window *w, struct display_line *dl,
			       emchar_dynarr *buf, int xpos, face_index findex,
			       int cursor);
static void tty_turn_on_face (struct window *w, face_index findex);
static void tty_turn_off_face (struct window *w, face_index findex);

/*****************************************************************************
 tty_font_metric_info

 tty's don't have fonts (that we use at least), so this actually
 ignores the font argument and just returns some constants.
 ****************************************************************************/
static void
tty_font_metric_info (struct device *d, Lisp_Object font,
		      struct font_metric_info *fm)
{
  fm->width = 1;
  fm->height = 1;
  fm->ascent = 1;
  fm->descent = 0;
  fm->proportional = 0;
}

/*****************************************************************************
 tty_text_width

 tty's don't have fonts (that we use at least), so everything is
 considered to be fixed width.  In other words, we just return len.
 ****************************************************************************/
static int
tty_text_width (struct window *w, Lisp_Object font, CONST Emchar *s,
		Charcount len)
{
#ifdef MULE
  /* !!####

     This might get more complicated.  Some extended characters
     have a width of 2. */
#endif
  return len;
}

/*****************************************************************************
 tty_divider_width

 Return the width of the vertical divider.  This is a function because
 divider_width is a device method.
 ****************************************************************************/
static int
tty_divider_width (void)
{
  return 1;
}

/*****************************************************************************
 tty_divider_height

 Return the width of the horizontal divider.  This is a function
 because divider_height is a device method.
 ****************************************************************************/
static int
tty_divider_height (void)
{
  return 1;
}

/*****************************************************************************
 tty_eol_cursor_width

 Return the width of the end-of-line cursor.  This is a function
 because eol_cursor_width is a device method.
 ****************************************************************************/
static int
tty_eol_cursor_width (void)
{
  return 1;
}

/*****************************************************************************
 tty_output_begin

 Perform any necessary initialization prior to an update.
 ****************************************************************************/
#ifdef DEBUG_XEMACS
void tty_output_begin (struct device *d);
void
#else
static void
#endif
tty_output_begin (struct device *d)
{
  /* Termcap requires this to be a global variable so we have to
     always set it for whatever tty device we are actually currently
     working with. */
  ospeed = DEVICE_TTY_DATA (d)->ospeed;
}

/*****************************************************************************
 tty_output_end

 Perform any necessary flushing of queues when an update has completed.
 ****************************************************************************/
#ifdef DEBUG_XEMACS
void tty_output_end (struct device *d);
void
#else
static void
#endif
tty_output_end (struct device *d)
{
  fflush (DEVICE_TTY_DATA (d)->outfd);
}

/*****************************************************************************
 tty_output_display_block

 Given a display line, a block number for that start line, output all
 runes between start and end in the specified display block.
 ****************************************************************************/
static void
tty_output_display_block (struct window *w, struct display_line *dl, int block,
			  int start, int end, int start_pixpos,
			  int cursor_start, int cursor_width,
			  int cursor_height)
{
  struct frame *f = XFRAME (w->frame);
  emchar_dynarr *buf = Dynarr_new (Emchar);

  struct display_block *db = Dynarr_atp (dl->display_blocks, block);
  rune_dynarr *rba = db->runes;
  struct rune *rb;

  int elt = start;
  face_index findex;
  int xpos;

  rb = Dynarr_atp (rba, elt);

  if (!rb)
    {
      /* Nothing to do so don't do anything. */
      return;
    }
  else
    {
      findex = rb->findex;
      xpos = rb->xpos;
    }

  if (end < 0)
    end = Dynarr_length (rba);

  Dynarr_reset (buf);

  while (elt < end && Dynarr_atp (rba, elt)->xpos < start_pixpos)
    {
      elt++;
      findex = Dynarr_atp (rba, elt)->findex;
      xpos = Dynarr_atp (rba, elt)->xpos;
    }

  while (elt < end)
    {
      rb = Dynarr_atp (rba, elt);

      if (rb->findex == findex && rb->type == CHAR && rb->object.ch != '\n'
	  && rb->cursor_type != CURSOR_ON)
	{
	  Dynarr_add (buf, rb->object.ch);
	  elt++;
	}
      else
	{
	  if (Dynarr_length (buf))
	    {
	      tty_output_string (w, dl, buf, xpos, findex, 0);
	      xpos = rb->xpos;
	    }
	  Dynarr_reset (buf);

	  if (rb->type == CHAR)
	    {
	      findex = rb->findex;
	      xpos = rb->xpos;

	      if (rb->object.ch == '\n')
		{
		  /* Clear in case a cursor was formerly here. */

		  Dynarr_add (buf, ' ');
		  tty_output_string (w, dl, buf, rb->xpos, DEFAULT_INDEX, 0);
		  Dynarr_reset (buf);

		  cmgoto (f, dl->ypos - 1, rb->xpos);

		  elt++;
		}
	      else if (rb->cursor_type == CURSOR_ON)
		{
		  /* There is not a distinct eol cursor on tty's. */

		  Dynarr_add (buf, rb->object.ch);
		  tty_output_string (w, dl, buf, xpos, findex, 0);
		  Dynarr_reset (buf);

		  cmgoto (f, dl->ypos - 1, xpos);

		  xpos += rb->width;
		  elt++;
		}
	    }
	  /* #### HLINE is actualy a little more complicated than this
             but at the moment it is only used to draw a turned off
             modeline and this will suffice for that. */
	  else if (rb->type == BLANK || rb->type == HLINE)
	    {
	      Emchar ch_to_add;
	      int size = rb->width;

	      if (rb->type == BLANK)
		ch_to_add = ' ';
	      else
		ch_to_add = '-';

	      while (size--)
		Dynarr_add (buf, ch_to_add);
	      tty_output_string (w, dl, buf, rb->xpos, findex, 0);

	      if (xpos >= cursor_start
		  && cursor_start < xpos + Dynarr_length (buf))
		{
		  cmgoto (f, dl->ypos - 1, cursor_start);
		}

	      Dynarr_reset (buf);

	      elt++;
	      if (elt < end)
		{
		  rb = Dynarr_atp (rba, elt);

		  findex = rb->findex;
		  xpos = rb->xpos;
		}
	    }
	  else if (rb->type == DGLYPH)
	    {
	      Lisp_Object window;
	      Lisp_Object instance;

	      XSETWINDOW (window, w);
	      instance = glyph_image_instance (rb->object.dglyph.glyph,
					       window, 1);

	      switch (XIMAGE_INSTANCE_TYPE (instance))
		{
		case IMAGE_TEXT:
		  {
		    /* #### This is way losing.  See the comment in
		       add_glyph_rune(). */
#ifdef MULE
		    /* lose; */
		    /* !!#### Chuck, you need to lower this stuff
		       to the device-independent level. */
		    Dynarr_add (buf, 'l');
		    Dynarr_add (buf, 'o');
		    Dynarr_add (buf, 's');
		    Dynarr_add (buf, 'e');
#else
		    Bytecount lossage;
		    Lisp_Object string =
		      XIMAGE_INSTANCE_TEXT_STRING (instance);
		    
		    for (lossage = 0;
			 lossage < string_length (XSTRING (string));
			 lossage++)
		      {
			if (lossage >= rb->object.dglyph.xoffset)
			  Dynarr_add (buf,
				      (Emchar) string_byte (XSTRING (string),
							    lossage));
		      }

		    tty_output_string (w, dl, buf, xpos, findex, 0);
		    
		    if (xpos >= cursor_start
			&& cursor_start < xpos + Dynarr_length (buf))
		      {
			cmgoto (f, dl->ypos - 1, cursor_start);
		      }
		    
		    Dynarr_reset (buf);
#endif
		  }
		  break;

		case IMAGE_MONO_PIXMAP:
		case IMAGE_COLOR_PIXMAP:
		case IMAGE_SUBWINDOW:
		  /* just do nothing here */
		  break;

		case IMAGE_CURSOR:
		  abort ();

		case IMAGE_NOTHING:
		  /* nothing is as nothing does */
		  break;

		default:
		  abort ();
		}

	      xpos += rb->width;
	      elt++;
	    }
	  else
	    abort ();
	}
    }

  if (Dynarr_length (buf))
    tty_output_string (w, dl, buf, xpos, findex, 0);
  Dynarr_free (buf);

}

/*****************************************************************************
 tty_output_vertical_divider

 Draw a vertical divider down the left side of the given window.
 ****************************************************************************/
static void
tty_output_vertical_divider (struct window *w, int clear)
{
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);
  int line;
  int y1 = WINDOW_TEXT_TOP (w);
  int y2 = WINDOW_TEXT_BOTTOM (w);

  for (line = y1; line < y2; line++)
    {
      cmgoto (f, line, WINDOW_TEXT_LEFT (w) - 1);
      fputc ('|', DEVICE_TTY_DATA (d)->outfd);
      TTY_INC_CURSOR_X (d, 1);
    }

  /* Draw the divider in the modeline. */
  cmgoto (f, y2, WINDOW_TEXT_LEFT (w) - 1);
  tty_turn_on_face (w, MODELINE_INDEX);
  fputc ('|', DEVICE_TTY_DATA (d)->outfd);
  TTY_INC_CURSOR_X (d, 1);
  tty_turn_off_face (w, MODELINE_INDEX);
}

/*****************************************************************************
 tty_clear_to_window_end

 Clear the area between ypos1 and ypos2.  Each margin area and the
 text area is handled separately since they may each have their own
 background color.
 ****************************************************************************/
static void
tty_clear_to_window_end (struct window *w, int ypos1, int ypos2)
{
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);
  int x, width;

  x = WINDOW_TEXT_LEFT (w);
  width = WINDOW_TEXT_WIDTH (w);

  if (window_is_rightmost (w))
    {
      /* #### Optimize to use clr_to_eol function of tty if available, if
	 the window is the entire width of the frame. */
      /* #### Is this actually an optimization? */
      int line;
      for (line = ypos1; line < ypos2; line++)
	{
	  cmgoto (XFRAME (w->frame), line, x);
	  OUTPUT1 (d, TTY_SE (d).clr_to_eol);
	}
    }
  else
    {
      Lisp_Object window;

      XSETWINDOW (window, w);
      tty_clear_region (window, DEFAULT_INDEX, x, ypos1, width, ypos2 - ypos1);
    }
}

/****************************************************************************
 tty_clear_region

 Clear the area in the box defined by the given parameters.
 ****************************************************************************/
void
tty_clear_region (Lisp_Object window, face_index findex, int x, int y,
		  int width, int height)
{
  struct window *w = XWINDOW (window);
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);
  int line;

  if (!width || !height)
     return;

  for (line = y; line < y + height; line++)
    {
      int col;

      cmgoto (f, line, x);

      if (window_is_leftmost (w)
	  && window_is_rightmost (w)
	  && TTY_SE (d).clr_to_eol)
	{
	  OUTPUT1 (d, TTY_SE (d).clr_to_eol);
	}
      else
	{
	  /* #### This should be static and adjusted as necessary. */
	  /* #### Of course, this is all complete and utter crap. */
	  bufbyte_dynarr *buf = Dynarr_new (Bufbyte);

	  for (col = x; col < x + width; col++)
	    Dynarr_add (buf, ' ');
	  Dynarr_add (buf, '\0');
	  fputs ((char *) Dynarr_atp (buf, 0), DEVICE_TTY_DATA (d)->outfd);
	  TTY_INC_CURSOR_X (d, Dynarr_length (buf));
	  Dynarr_free (buf);
	}
    }

  cmgoto (f, y, x);
}

/****************************************************************************
 tty_clear_frame

 Clear the entire frame.
 ****************************************************************************/
static void
tty_clear_frame (struct frame *f)
{
  struct device *d = XDEVICE (f->device);

  if (TTY_SE (d).clr_frame)
    {
      OUTPUT1 (d, TTY_SE (d).clr_frame);
#ifdef NOT_SURE
      FRAME_CURSOR_X (f) = 0;
      FRAME_CURSOR_Y (f) = 0;
#endif
    }
  else
    {
#ifdef NOT_SURE
      internal_cursor_to (f, 0, 0);
      clear_to_end (f);
#else
      /* #### Not implemented. */
      fprintf (stderr, "Not yet.\n");
#endif
    }
}

/*****************************************************************************
 tty_output_string

 Given a string and a starting position, output that string in the
 given face.  If cursor is true, draw a cursor around the string.
 ****************************************************************************/
static void
tty_output_string (struct window *w, struct display_line *dl,
		   emchar_dynarr *buf, int xpos, face_index findex, int cursor)
{
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);

  /* First position the cursor. */
  cmgoto (f, dl->ypos - 1, xpos);

  /* Enable any face properties. */
  tty_turn_on_face (w, findex);

  /* Output the string. */
#ifdef MULE
  {
    int i;
    Emchar *s = Dynarr_atp (buf, 0);
    int len = Dynarr_length (buf);
    char *one_byte_string = (char *) alloca (len + 1);
    for (i = 0; i < len; i++)
      {
	if (s[i] < 0x80)
	  one_byte_string[i] = (char) s[i];
	else
	  one_byte_string[i] = 'X';
      }
    one_byte_string[len] = '\0';
    /* #### Ben sez: need a level of abstraction here to handle
       the termscript and such.

       #### Ben also sez: don't some terminals need nulls outputted
       for proper timing? */
    fputs (one_byte_string, DEVICE_TTY_DATA (d)->outfd);
    TTY_INC_CURSOR_X (d, len);
  }
  /* !!#### presumably there are some escape sequences blah blah that can
     be issued to support kterms and such.  Need to figure this out.
     I think actually that kterms support both JIS and EUC encoding
     (they can coexist at the same time because EUC uses entirely
     characters with the high-bit set and JIS uses entirely seven-bit
     characters). */
#else
  {
    int i;
    Emchar *s = Dynarr_atp (buf, 0);
    int len = Dynarr_length (buf);
    char *one_byte_string = (char *) alloca (len + 1);
    for (i = 0; i < len; i++)
      {
	/* #### perhaps should use DASSERT? */
	assert (s[i] < 0x100);
	one_byte_string[i] = (char) s[i];
      }
    one_byte_string[len] = '\0';
    /* #### Ben sez: need a level of abstraction here to handle
       the termscript and such.

       #### Ben also sez: don't some terminals need nulls outputted
       for proper timing? */
    fputs (one_byte_string, DEVICE_TTY_DATA (d)->outfd);
    TTY_INC_CURSOR_X (d, len);
  }
#endif

  /* Turn the face properties back off. */
  tty_turn_off_face (w, findex);
}

/*****************************************************************************
 tty_turn_on_face

 Turn on all set properties of the given face.
 ****************************************************************************/
static void
tty_turn_on_face (struct window *w, face_index findex)
{
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);

  /* #### still need to handle color */

  if (FACE_CACHE_ELEMENT_HIGHLIGHT_P (w, findex))
    {
      OUTPUT1_IF (d, TTY_SD (d).turn_on_bold);
    }

  if (FACE_CACHE_ELEMENT_REVERSE_P (w, findex))
    {
      /* #### punt for now if standout mode is glitchy */
      if (!TTY_FLAGS (d).standout_width)
	{
	  OUTPUT1_IF (d, TTY_SD (d).begin_standout);
	}
    }

  if (FACE_CACHE_ELEMENT_BLINKING_P (w, findex))
    {
      OUTPUT1_IF (d, TTY_SD (d).turn_on_blinking);
    }

  if (FACE_CACHE_ELEMENT_DIM_P (w, findex))
    {
      OUTPUT1_IF (d, TTY_SD (d).turn_on_dim);
    }

  if (FACE_CACHE_ELEMENT_UNDERLINE_P (w, findex))
    {
      /* #### punt for now if underline mode is glitchy */
      if (!TTY_FLAGS (d).underline_width)
	{
	  OUTPUT1_IF (d, TTY_SD (d).begin_underline);
	}
    }
}

/*****************************************************************************
 tty_turn_off_face

 Turn off all set properties of the given face (revert to default
 face).  We assume that tty_turn_on_face has been called for the given
 face so that it's properties are actually active.
 ****************************************************************************/
static void
tty_turn_off_face (struct window *w, face_index findex)
{
  struct frame *f = XFRAME (w->frame);
  struct device *d = XDEVICE (f->device);

  if (FACE_CACHE_ELEMENT_UNDERLINE_P (w, findex))
    {
      /* #### punt for now if underline mode is glitchy */
      if (!TTY_FLAGS (d).underline_width)
	{
	  OUTPUT1_IF (d, TTY_SD (d).end_underline);
	}
    }

  if (FACE_CACHE_ELEMENT_HIGHLIGHT_P (w, findex) ||
      FACE_CACHE_ELEMENT_BLINKING_P (w, findex) ||
      FACE_CACHE_ELEMENT_DIM_P (w, findex))
    {
      OUTPUT1_IF (d, TTY_SD (d).turn_off_attributes);
    }

  if (FACE_CACHE_ELEMENT_REVERSE_P (w, findex))
    {
      /* #### punt for now if standout mode is glitchy */
      if (!TTY_FLAGS (d).standout_width)
	{
	  OUTPUT1_IF (d, TTY_SD (d).end_standout);
	}
    }
}

/*****************************************************************************
 set_tty_modes

 Sets up various parameters on tty modes.
 ****************************************************************************/
void
set_tty_modes (struct device *d)
{
  if (!DEVICE_IS_TTY (d))
    return;

  OUTPUT1_IF (d, TTY_SD (d).init_motion);
  OUTPUT1_IF (d, TTY_SD (d).cursor_visible);
  OUTPUT1_IF (d, TTY_SD (d).keypad_on);
}

/*****************************************************************************
 reset_tty_modes

 Restore default state of tty.
 ****************************************************************************/
void
reset_tty_modes (struct device *d)
{
  if (!DEVICE_IS_TTY (d))
    return;

  OUTPUT1_IF (d, TTY_SD (d).keypad_off);
  OUTPUT1_IF (d, TTY_SD (d).cursor_normal);
  OUTPUT1_IF (d, TTY_SD (d).end_motion);
}


/* #### Everything below here is old shit.  It should either be moved
   up or removed. */


/* FLAGS - these don't need to be device local since only one device
 	   can be being updated at a time. */
static int insert_mode_on;		/* nonzero if in insert mode */
static int standout_mode_on;		/* nonzero if in standout mode */
static int underline_mode_on;		/* nonzero if in underline mode */
static int alternate_mode_on;		/* nonzero if in alternate char set */
static int attributes_on;		/* nonzero if any attributes on */

#ifdef NOT_YET
static void
turn_on_insert (struct frame *f)
{
  struct device *d = XDEVICE (f->device);

  if (!insert_mode_on)
    OUTPUT1_IF (d, TTY_SE (d).begin_ins_mode);
  insert_mode_on = 1;
}

static void
turn_off_insert (struct frame *f)
{
  struct device *d = XDEVICE (f->device);

  if (insert_mode_on)
    OUTPUT1 (d, TTY_SE (d).end_ins_mode);
  insert_mode_on = 0;
}

static void
internal_cursor_to (struct frame *f, int row, int col)
{
  struct device *d = XDEVICE (f->device);

  if (!TTY_FLAGS (d).insert_mode_motion)
    turn_off_insert (f);
  if (!TTY_FLAGS (d).standout_motion)
    {
      turn_off_standout (f);
      turn_off_underline (f);
      turn_off_alternate (f);
    }

  cmgoto (f, row, col);
}

static void
clear_to_end (struct frame *f)
{
  struct device *d = XDEVICE (f->device);

  /* assumes cursor is already positioned */
  if (TTY_SE (d).clr_from_cursor)
    {
      OUTPUT1 (d, TTY_SE (d).clr_from_cursor);
    }
  else
    {
      int line = FRAME_CURSOR_Y (f);

      while (line < FRAME_HEIGHT (f))
	{
	  internal_cursor_to (f, line, 0);
	  OUTPUT1 (d, TTY_SE (d).clr_to_eol);
	}
    }
}
#endif /* 0 */

#if 0
/*
 *  clear from last visible line on window to window end (presumably
 *  the line above window's modeline
 */
static void
tty_clear_window_end (struct window *w, int ystart, int yend)
{
  struct device *d = XDEVICE (XFRAME (w->frame)->device);
  int line;

  for (line = ystart; line < yend; line++)
    {
      cmgoto (XFRAME (w->frame), line, 0);
      OUTPUT1 (d, TTY_SE (d).clr_to_eol);
    }
}

/*
 * tty_shift_region - shift a region between START and END to its new position
 */
static void
tty_shift_region (struct window *w, struct line_header *start,
		  struct line_header *end)
{
  stderr_out ("tty_shift_region:  NOT YET IMPLEMENTED\n");
}
#endif /* 0 */

#ifdef OLD_REDISPLAY_SHIT

static void
tty_update_begin (struct window *w)
{
}

static void
tty_update_end (struct window *w)
{
  struct frame *f = XFRAME (w->frame);

  /* reset state */
  turn_off_insert (f);
  turn_off_standout (f);
  turn_off_underline (f);
  turn_off_alternate (f);
  turn_off_attributes (f);

  internal_cursor_to (f, f->cursor_y, f->cursor_x);

  tty_flush (XDEVICE (f->device));
}

static void
tty_cursor_to (struct line_header *l, struct char_block *cb, int row, int col,
	       struct window_mirror *mir, struct frame *f)
{
  internal_cursor_to (f, l->ypos, cb->xpos);
}

#endif /* OLD_REDISPLAY_SHIT */

static int
tty_flash (struct device *d)
{
  if (TTY_SD (d).visual_bell)
    {
      OUTPUT1 (d, TTY_SD (d).visual_bell);
      fflush (DEVICE_TTY_DATA (d)->outfd);
      return 1;
    }
  else
    return 0;
}

/*
 * tty_ring_bell - sound an audio beep.
 */
static void
tty_ring_bell (struct device *d, int volume, int pitch, int duration)
{
  OUTPUT1 (d, TTY_SD (d).audio_bell);
  fflush (DEVICE_TTY_DATA (d)->outfd);
}

int
init_tty_for_redisplay (struct device *d, char *terminal_type)
{
  int status;
  char entry_buffer[2044];
  /* char temp_buffer[2044]; */
  char *bufptr;

  /* What we should really do is allocate just enough space for
     the actual strings that are stored; but this would require
     doing this after all the tgetstr()s and adjusting all the
     pointers. */
  DEVICE_TTY_DATA (d)->term_entry_buffer = (char *) xmalloc (2044);
  bufptr = DEVICE_TTY_DATA (d)->term_entry_buffer; 

  status = tgetent (entry_buffer, terminal_type);
  if (status < 0)
    return TTY_UNABLE_OPEN_DATABASE;
  else if (status == 0)
    return TTY_TYPE_UNDEFINED;

  /*
   * Establish the terminal size.
   */
  /* First try to get the info from the system.  If that fails, check
     the termcap entry. */
  get_tty_device_size (d, &DEVICE_TTY_DATA(d)->width,
		       &DEVICE_TTY_DATA(d)->height);

  if (DEVICE_TTY_DATA (d)->width <= 0)
    DEVICE_TTY_DATA (d)->width = tgetnum ("co");
  if (DEVICE_TTY_DATA (d)->height <= 0)
    DEVICE_TTY_DATA (d)->height = tgetnum ("li");

  if (DEVICE_TTY_DATA (d)->width <= 0 || DEVICE_TTY_DATA (d)->height <= 0)
    return TTY_SIZE_UNSPECIFIED;

  /*
   * Initialize cursor motion information.
   */

  /* local cursor movement */
  TTY_CM (d).up = tgetstr ("up", &bufptr);
  TTY_CM (d).down = tgetstr ("do", &bufptr);
  TTY_CM (d).left = tgetstr ("le", &bufptr);
  TTY_CM (d).right = tgetstr ("nd", &bufptr);
  TTY_CM (d).home = tgetstr ("ho", &bufptr);
  TTY_CM (d).low_left = tgetstr ("ll", &bufptr);
  TTY_CM (d).car_return = tgetstr ("cr", &bufptr);

  /* absolute cursor motion */
  TTY_CM (d).abs = tgetstr ("cm", &bufptr);
  TTY_CM (d).hor_abs = tgetstr ("ch", &bufptr);
  TTY_CM (d).ver_abs = tgetstr ("cv", &bufptr);

  /* Verify that the terminal is powerful enough to run Emacs */
  if (!TTY_CM (d).abs)
    {
      if (!TTY_CM (d).up || !TTY_CM (d).down
	  || !TTY_CM (d).left || !TTY_CM (d).right)
	return TTY_TYPE_INSUFFICIENT;
    }

  /* parameterized local cursor movement */
  TTY_CM (d).multi_up = tgetstr ("UP", &bufptr);
  TTY_CM (d).multi_down = tgetstr ("DO", &bufptr);
  TTY_CM (d).multi_left = tgetstr ("LE", &bufptr);
  TTY_CM (d).multi_right = tgetstr ("RI", &bufptr);

  /* scrolling */
  TTY_CM (d).scroll_forw = tgetstr ("sf", &bufptr);
  TTY_CM (d).scroll_back = tgetstr ("sr", &bufptr);
  TTY_CM (d).multi_scroll_forw = tgetstr ("SF", &bufptr);
  TTY_CM (d).multi_scroll_back = tgetstr ("SR", &bufptr);
  TTY_CM (d).set_scroll_region = tgetstr ("cs", &bufptr);


  /*
   * Initialize screen editing information.
   */

  /* adding to the screen */
  TTY_SE (d).ins_line = tgetstr ("al", &bufptr);
  TTY_SE (d).multi_ins_line = tgetstr ("AL", &bufptr);
  TTY_SE (d).repeat = tgetstr ("rp", &bufptr);
  TTY_SE (d).begin_ins_mode = tgetstr ("im", &bufptr);
  TTY_SE (d).end_ins_mode = tgetstr ("ei", &bufptr);
  TTY_SE (d).ins_char = tgetstr ("ic", &bufptr);
  TTY_SE (d).multi_ins_char = tgetstr ("IC", &bufptr);
  TTY_SE (d).insert_pad = tgetstr ("ip", &bufptr);

  /* deleting from the screen */
  TTY_SE (d).clr_frame = tgetstr ("cl", &bufptr);
  TTY_SE (d).clr_from_cursor = tgetstr ("cd", &bufptr);
  TTY_SE (d).clr_to_eol = tgetstr ("ce", &bufptr);
  TTY_SE (d).del_line = tgetstr ("dl", &bufptr);
  TTY_SE (d).multi_del_line = tgetstr ("DL", &bufptr);
  TTY_SE (d).del_char = tgetstr ("dc", &bufptr);
  TTY_SE (d).multi_del_char = tgetstr ("DC", &bufptr);
  TTY_SE (d).begin_del_mode = tgetstr ("dm", &bufptr);
  TTY_SE (d).end_del_mode = tgetstr ("ed", &bufptr);
  TTY_SE (d).erase_at_cursor = tgetstr ("ec", &bufptr);


  /*
   * Initialize screen display information.
   */
  TTY_SD (d).begin_standout = tgetstr ("so", &bufptr);
  TTY_SD (d).end_standout = tgetstr ("se", &bufptr);
  TTY_SD (d).begin_underline = tgetstr ("us", &bufptr);
  TTY_SD (d).end_underline = tgetstr ("ue", &bufptr);
  TTY_SD (d).begin_alternate = tgetstr ("as", &bufptr);
  TTY_SD (d).end_alternate = tgetstr ("ae", &bufptr);
  TTY_SD (d).turn_on_reverse = tgetstr ("mr", &bufptr);
  TTY_SD (d).turn_on_blinking = tgetstr ("mb", &bufptr);
  TTY_SD (d).turn_on_bold = tgetstr ("md", &bufptr);
  TTY_SD (d).turn_on_dim = tgetstr ("mh", &bufptr);
  TTY_SD (d).turn_off_attributes = tgetstr ("me", &bufptr);

  TTY_SD (d).visual_bell = tgetstr ("vb", &bufptr);
  TTY_SD (d).audio_bell = tgetstr ("bl", &bufptr);
  if (!TTY_SD (d).audio_bell)
    {
      /* If audio_bell doesn't get set, then assume C-g.  This is gross and
         ugly but is what Emacs has done from time immortal. */
      TTY_SD (d).audio_bell = "\07";
    }

  TTY_SD (d).cursor_visible = tgetstr ("ve", &bufptr);
  TTY_SD (d).cursor_normal = tgetstr ("vs", &bufptr);
  TTY_SD (d).init_motion = tgetstr ("ti", &bufptr);
  TTY_SD (d).end_motion = tgetstr ("te", &bufptr);
  TTY_SD (d).keypad_on = tgetstr ("ks", &bufptr);
  TTY_SD (d).keypad_off = tgetstr ("ke", &bufptr);


  /*
   * Initialize additional terminal information.
   */
  TTY_FLAGS (d).must_write_spaces = tgetflag ("in");
  TTY_FLAGS (d).insert_mode_motion = tgetflag ("mi");
  TTY_FLAGS (d).standout_motion = tgetflag ("ms");
  TTY_FLAGS (d).memory_above_frame = tgetflag ("da");
  TTY_FLAGS (d).memory_below_frame = tgetflag ("db");
  TTY_FLAGS (d).meta_key = tgetflag ("km") || tgetflag ("MT");
  TTY_FLAGS (d).standout_width = tgetnum ("sg");
  TTY_FLAGS (d).underline_width = tgetnum ("ug");

  if (TTY_FLAGS (d).standout_width == -1)
    TTY_FLAGS (d).standout_width = 0;
  if (TTY_FLAGS (d).underline_width == -1)
    TTY_FLAGS (d).underline_width = 0;

  /*
   * Setup the costs tables for this tty device.
   */
  cm_cost_init (d);

  /*
   * Initialize local flags.
   */
  insert_mode_on = 0;
  standout_mode_on = 0;
  underline_mode_on = 0;
  alternate_mode_on = 0;
  attributes_on = 0;

  /* #### fix this */
  DEVICE_CLASS (d) = Qmono;

  return TTY_INIT_SUCCESS;
}


/************************************************************************/
/*                            initialization                            */
/************************************************************************/

void
device_type_create_redisplay_tty (void)
{
  /* redisplay methods */
  DEVICE_HAS_METHOD (tty, text_width);
  DEVICE_HAS_METHOD (tty, font_metric_info);
  DEVICE_HAS_METHOD (tty, output_display_block);
  DEVICE_HAS_METHOD (tty, output_vertical_divider);
  DEVICE_HAS_METHOD (tty, divider_width);
  DEVICE_HAS_METHOD (tty, divider_height);
  DEVICE_HAS_METHOD (tty, eol_cursor_width);
  DEVICE_HAS_METHOD (tty, clear_to_window_end);
  DEVICE_HAS_METHOD (tty, clear_region);
  DEVICE_HAS_METHOD (tty, clear_frame);
  DEVICE_HAS_METHOD (tty, output_begin);
  DEVICE_HAS_METHOD (tty, output_end);
  DEVICE_HAS_METHOD (tty, flash);
  DEVICE_HAS_METHOD (tty, ring_bell);
}
