/* Stream device functions.
   Copyright (C) 1995 Amdahl Corporation

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: Not in FSF. */

/* This file has been Mule-ized. */

/* Written by Ben Wing. */

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

#include "device-stream.h"
#include "events.h"
#include "frame.h"
#include "redisplay.h"
#include "sysdep.h"

DEFINE_DEVICE_TYPE (stream);

Lisp_Object Vterminal_device;
Lisp_Object Vterminal_frame;

static void
allocate_stream_device_struct (struct device *d)
{
  d->device_data =
    (struct stream_device *) xmalloc (sizeof (struct stream_device));

  /* zero out all slots. */
  memset (d->device_data, 0, sizeof (struct stream_device));
}

static void
stream_init_device (struct device *d, Lisp_Object device_data)
{
  Lisp_Object tty = Qnil;
  FILE *infd, *outfd, *errfd;

  tty = Fnth (Qzero, device_data);

  /* Open the specified device */

  if (NILP (tty))
    {
      infd = stdin;
      outfd = stdout;
      errfd = stderr;
    }
  else
    {
      CHECK_STRING (tty, 0);
      infd = outfd = errfd =
	fopen ((char *) string_data (XSTRING (tty)), "r+");
      if (!infd)
	error ("Unable to open tty %s", string_data (XSTRING (tty)));
    }

  allocate_stream_device_struct (d);
  DEVICE_STREAM_DATA (d)->infd = infd;
  DEVICE_STREAM_DATA (d)->outfd = outfd;
  DEVICE_STREAM_DATA (d)->errfd = errfd;
  DEVICE_INFD (d) = fileno (infd);
  DEVICE_OUTFD (d) = fileno (outfd);
  init_baud_rate (d);
  init_one_device (d);
}

static int
stream_initially_selected_for_input (struct device *d)
{
  return noninteractive && initialized;
}

static void
free_stream_device_struct (struct device *d)
{
  struct stream_device *td = (struct stream_device *) d->device_data;
  if (td)
    xfree (td);
}

extern int stdout_needs_newline;

static void
stream_delete_device (struct device *d)
{
  if (/* DEVICE_STREAM_DATA (d)->needs_newline */
      stdout_needs_newline) /* #### clean this up */    
    {
      fputc ('\n', DEVICE_STREAM_DATA (d)->outfd);
      fflush (DEVICE_STREAM_DATA (d)->outfd);
    }
  if (DEVICE_STREAM_DATA (d)->infd != stdin)
    fclose (DEVICE_STREAM_DATA (d)->infd);
  free_stream_device_struct (d);
}


static void
stream_init_frame (struct frame *f, Lisp_Object parms)
{
  struct device *d = XDEVICE (FRAME_DEVICE (f));
  if (!NILP (DEVICE_FRAME_LIST (d)))
    error ("Only one frame allowed on stream devices");

  f->name = build_string ("stream");
  f->height = 80;
  f->width = 24;
  f->visible = 0; /* so redisplay doesn't try to do anything */
}


static void
stream_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;
}

static int
stream_text_width (struct window *w, Lisp_Object font,
		   CONST Emchar *s, int len)
{
  return len;
}

static int
stream_left_margin_width (struct window *w)
{
  return 0;
}

static int
stream_right_margin_width (struct window *w)
{
  return 0;
}

static int
stream_divider_width (void)
{
  return 1;
}

static int
stream_divider_height (void)
{
  return 1;
}

static int
stream_eol_cursor_width (void)
{
  return 1;
}

static void
stream_output_begin (struct device *d)
{
}

static void
stream_output_end (struct device *d)
{
}

static void
stream_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)
{
}

static void
stream_output_vertical_divider (struct window *w, int clear)
{
}

static void
stream_clear_to_window_end (struct window *w, int ypos1, int ypos2)
{
}

static void
stream_clear_region (Lisp_Object locale, face_index findex, int x, int y,
		       int width, int height)
{
}

static void
stream_clear_frame (struct frame *f)
{
}

static int
stream_flash (struct device *d)
{
  return 0; /* sorry can't do it */
}

static void
stream_ring_bell (struct device *d, int volume, int pitch, int duration)
{
  fputc (07, DEVICE_STREAM_DATA (d)->outfd);
  fflush (DEVICE_STREAM_DATA (d)->outfd);
}

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

void
device_type_create_stream (void)
{
  INITIALIZE_DEVICE_TYPE (stream, "stream", "device-stream-p");

  /* device methods */
  DEVICE_HAS_METHOD (stream, init_device);
  DEVICE_HAS_METHOD (stream, initially_selected_for_input);
  DEVICE_HAS_METHOD (stream, delete_device);

  /* frame methods */
  DEVICE_HAS_METHOD (stream, init_frame);

  /* redisplay methods */
  DEVICE_HAS_METHOD (stream, left_margin_width);
  DEVICE_HAS_METHOD (stream, right_margin_width);
  DEVICE_HAS_METHOD (stream, text_width);
  DEVICE_HAS_METHOD (stream, font_metric_info);
  DEVICE_HAS_METHOD (stream, output_display_block);
  DEVICE_HAS_METHOD (stream, output_vertical_divider);
  DEVICE_HAS_METHOD (stream, divider_width);
  DEVICE_HAS_METHOD (stream, divider_height);
  DEVICE_HAS_METHOD (stream, eol_cursor_width);
  DEVICE_HAS_METHOD (stream, clear_to_window_end);
  DEVICE_HAS_METHOD (stream, clear_region);
  DEVICE_HAS_METHOD (stream, clear_frame);
  DEVICE_HAS_METHOD (stream, output_begin);
  DEVICE_HAS_METHOD (stream, output_end);
  DEVICE_HAS_METHOD (stream, flash);
  DEVICE_HAS_METHOD (stream, ring_bell);
}

void
vars_of_device_stream (void)
{
  DEFVAR_LISP ("terminal-device", &Vterminal_device,
    "The initial device-object, which represent's Emacs's stdout.");
  Vterminal_device = Qnil;

  DEFVAR_LISP ("terminal-frame", &Vterminal_frame,
    "The initial frame-object, which represents Emacs's stdout.");
  Vterminal_frame = Qnil;
}

void
init_device_stream (void)
{
  /* This function can GC */
  if (!initialized)
    {
      Vterminal_device = Fmake_device (Qstream, Qnil);
      Vterminal_frame = Fmake_frame (Qnil, Vterminal_device);
    }
  else if (noninteractive)
    event_stream_select_device (XDEVICE (Vterminal_device));
}
