/*
 *		STIO.C
 * These functions negotiate with various flavors atariST TOS/MiNT
 *   system for characters and write characters in a barely buffered fashion
 *   to the display.
 * Signals are also handled for MiNT only.
 *
 * Most of this code cribbed from unixio.c and pcio.c
 *	++jrb	bammi@cadence.com
 * Public Domain
 */

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


/***************************************************************************/
/*****	The top section of this file is for TOS, the bottom for MiNT *******/
/***************************************************************************/

#ifndef __MINT__
/***************************************************************************/
/*****				TOS section			     *******/
/***************************************************************************/

#include <osbind.h>
#include <sysvars.h>

/* ******************************************************************** */
/* ******** Opening and closing the terminal ************************** */
/* ******************************************************************** */

/*
 * This function is called once to set up the terminal device streams.
 */
ttopen()
{
}

/*
 * This function gets called just before we exit ME.
 */
ttclose()
{
}

/* ******************************************************************** */
/* ************** Terminal I/O  *************************************** */
/* ******************************************************************** */

/*
 * Write a character to the display.
 * Use the rawest console output routine that handles backspace, \r and \n.
 */
t_putchar(c) int c; { Bconout(2, c); }

/*
 * Flush terminal buffer. Does real work when the terminal output is
 *   buffered up.
 */
t_flush() {  }

/*
 * Read a character from the terminal, performing no editing and doing no
 *   echo at all.
 * Map terminal dependent things (like softkeys and other things ME doesn't
 *   or can't know about) to ME keys.  Do *NOT* map control keys.
 * In general, try just to read a character and return it.
 */
KeyCode t_getchar()
{
  unsigned long kee = Bconin(2);
  short *key = (short *)&kee;
  int scancode = *key;
  int c = *(key+1);
  KeyCode keycode;

  if(c == 0 && scancode != 0 && map_key(scancode, &keycode))
	return keycode;
  else
	return (KeyCode)c;
}

    /* Ask the window/screen size how big it is.
     * Input:
     *   row:  Pointer to a int
     *   col:  Pointer to a int
     * Munges:
     *   row:  The number of rows on the window or screen is put here.
     *   col:  The number of columns on the window or screen is put here.
     * Returns:
     *   TRUE:  If I know how to do this and row,col updated.
     *   FALSE: Not supported or got bogus data.  row, col not changed.
     */
int get_size_from_window(row, col) int *row, *col;
{
  return FALSE;
}

/* ******************************************************************** */
/* *********** Key waiting? ******************************************* */
/* ******************************************************************** */

   /* Wait for a key to be pressed for no more than sec seconds.
    * Use 0 seconds to check to see if a key is in the input que.
    */
wait_for_key(sec)
{
  long waitUntil = get_sysvar(_hz_200) + (((long)sec) << 8);

  do {
	if(Bconstat(2)) return TRUE;
  } while (get_sysvar(_hz_200) < waitUntil);
  return FALSE;
}

/* keywaiting:
 * Check to see if any characters are already in the keyboard buffer.
 */
keywaiting()
{
  return Bconstat(2);
}


#else /* __MINT__ */

/***************************************************************************/
/*****				MiNT section			     *******/
/***************************************************************************/

#include <sgtty.h>		/* for stty/gtty functions */
#include <sys/ioctl.h>		/* to get at the typeahead */

struct sgttyb ostate;		/* saved tty state */
struct sgttyb nstate;		/* values for editor mode */
struct tchars otchars;		/* Saved terminal special character set */
struct tchars ntchars =		/* A lot of nothing */
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };


/* ******************************************************************** */
/* ******** Opening and closing the terminal ************************** */
/* ******************************************************************** */

/*
 * This function is called once to set up the terminal device streams.
 */
ttopen()
{
  gtty(0,&ostate);			/* save old state */
  gtty(0,&nstate);			/* get base of new state */
  nstate.sg_flags |= RAW;
/*  nstate.sg_flags &= ~(ECHO | CRMOD);	/* no echo for now... */
nstate.sg_flags &= ~(ECHO | CRMOD | TANDEM);	/* no echo for now... */
  stty(0, &nstate);			/* set mode */
  ioctl(0, TIOCGETC, &otchars);		/* Save old characters */
  ioctl(0, TIOCSETC, &ntchars);		/* Place new character into K */

  set_signals();
}

/*
 * This function gets called just before we exit ME.
 */
ttclose()
{
  stty(0,&ostate);
  ioctl(0,TIOCSETC,&otchars);		/* Place old character into K */
}

/* ******************************************************************** */
/* ************** Terminal I/O  *************************************** */
/* ******************************************************************** */

/*
 * Write a character to the display.
 * Use the rawest console output routine that handles backspace, \r and \n.
 */
t_putchar(c) unsigned char c; { fputc(c,stdout); }

/*
 * Flush terminal buffer. Does real work when the terminal output is
 *   buffered up.
 */
t_flush() { fflush(stdout); }

/*
 * Read a character from the terminal, performing no editing and doing no
 *   echo at all.
 * Map terminal dependent things (like softkeys and other things ME doesn't
 *   or can't know about) to ME keys.  Do *NOT* map control keys.
 * In general, try just to read a character and return it.
 * Note:
 *   On Unix systems, system calls (like read()) can be interrupted.  In
 *     this case, read() will return -1 and errno will be set to EINTR.
 */
KeyCode t_getchar()
{
  extern int errno;

  unsigned char c;
  int n;
  KeyCode keycode;

  while (TRUE)
  {
    n = read(0,&c,1);
    if (0 < n) return (KeyCode)c;	/* the expected case */

	/* Nothing read, error or (most likely) a signal.
	 * Since it might be a signal that something needs to be done and we
	 *   aren't anything else right now, try to process any hooks.
	 * Note:  On some OSs, read() is automatically restarted so we will
	 *   never get this case.
	 */
    process_hooks();
  }
}

    /* Ask the window/screen size how big it is.
     * Input:
     *   row:  Pointer to a int
     *   col:  Pointer to a int
     * Munges:
     *   row:  The number of rows on the window or screen is put here.
     *   col:  The number of columns on the window or screen is put here.
     * Returns:
     *   TRUE:  If I know how to do this and row,col updated.
     *   FALSE: Not supported or got bogus data.  row, col not changed.
     * Notes:
     *   According to the BSD tty(4) man page:  If, for some unknown (to me)
     *     reason, the terminal has bogus data, the winsize structure is
     *     zeroed.  On Apollo, this happens in special cases.  ws_xpixel and
     *     ws_ypixel always seem to be zero (probably something to do with
     *     my terminal windows).
     */
int get_size_from_window(row, col) int *row, *col;
{
#ifdef TIOCGWINSZ

  struct winsize term_stats;			/* <sys/ioctl.h> */

	/* Get the terminal size */
  if (-1 == ioctl(0, TIOCGWINSZ, &term_stats) || 0 == term_stats.ws_row)
	return FALSE;		/* something bad happened */

  *row = term_stats.ws_row;
  *col = term_stats.ws_col;

  return TRUE;

#else

  return FALSE;

#endif
}

/* ******************************************************************** */
/* *********** Key waiting? ******************************************* */
/* ******************************************************************** */

/* Note:  need 1 or 2 routines to check to see if a key is in the input que.
 *   One routine is needed if it is reliable and fast or you need a fast
 *   routine (for HP escape sequence parsing) and one thats fast (for
 *   interrupting a display update).
 * Sync these up with config.h.
 */
#include <time.h>

   /* Wait for a key to be pressed for no more than sec seconds.
    * Use 0 seconds to check to see if a key is in the input que.
    */
wait_for_key(sec)
{
  int x = 1;		/* stdin */
  struct timeval timeout;

  timeout.tv_sec = sec; timeout.tv_usec = 0;
  return select(1, &x, 0, 0, &timeout);
}

/* keywaiting:
 * Check to see if any characters are already in the keyboard buffer.
 * This is reliable but slows things down if called much.
 * This HAS to work.  Sometimes used to parse (HP like) keyboard generated
 *   escape sequences.
 * Note: FIONREAD is no good on HPUX.
 */
keywaiting()
{
  long int x;
  return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
}

/* ******************************************************************** */
/* ******************* Signals **************************************** */
/* ******************************************************************** */

#include <signal.h>

#ifdef SIGWINCH

    /* The screen has been resized.
     * WARNING:  Should NOT actually resize the display in a signal handler
     *   because ME could be in the middle of doing all kinds of things and
     *   this could really mess things up (I don't know for sure but I don't
     *   want to have to debug that case!) or the signal might arrive before
     *   the display system has been properly initialized.
     * Result:
     *   Tell the main loop to call us when it can.
     */
static void sig_resize(dummy)
{
  void sig_do_the_resize();

  call_me_sometime(RESIZE_HOOK,sig_do_the_resize);
}

static void sig_do_the_resize()
{
  int row, col;

  if (get_size_from_window(&row, &col))
  {
    display_resized(row,col);
    update();		/* force the screen to be redrawn */
  }
}

#endif

    /* An external process has told ME that it has some info.
     */
static void sig_expro(foo)
{
  extern void process_talks();

  signal(SIGUSR1, SIG_IGN);
  call_me_sometime(PROCESS_HOOK,process_talks);
  signal(SIGUSR1, sig_expro);		/* reset the signal */
}

  /* Somebody, somewhere, told me to bite the big one.  Well, I can take a
   *   hint.
   * If there are any modified buffers, too bad.  I suppose I could have a
   *   ME-quit-hook.
   */
static void sig_die(dummy)
{
  mlwrite("ME2 terminated via signal.\r\n");
  quit(TRUE,1);
}

  /* Put ME into the background.  This can be invoked via signal or called
   *   from ME.  This means I gotta be careful when I send myself a signal.
   */
void sig_pause(dummy)
{
  close_display(TRUE);	     /* restore terminal for normal user interface */

  kill(getpid(),SIGSTOP);	/* hello background.  Can't catch SIGSTOP. */

  open_display();		/* set up terminal for editor */
    /* ME might not be in the keywait loop when this is called (because of
     *   an external signal) and so might not redraw the screen until a key
     *   is pressed.
     */
  update();		/* force the screen to be redrawn */
}

  /* Setup the signals I'm going to catch:
   *   Signal	Action
   *   SIGTSTP	Put ME into the background.
   *   SIGTERM	Clean up and exit.
   *   SIGHUP	Clean up and exit.
   *   SIGINT	Clean up and exit.
   *   SIGUSR1  Unused.
   *   SIGUSR2  Unused.
   *   SIGWINCH Resize the screen.
   * Ignore these signals:
   *   SIGCONT  sig_pause() will take care of everything it would do.
   */
static void set_signals()
{
  signal(SIGTSTP,sig_pause);
  signal(SIGCONT,SIG_IGN);

  signal(SIGTERM,sig_die);
  signal(SIGHUP, sig_die);
  signal(SIGINT, sig_die);

#ifdef SIGWINCH
  signal(SIGWINCH, sig_resize);
#endif

  signal(SIGUSR1, sig_expro);
  signal(SIGUSR2, SIG_IGN);
}
#endif /* MiNT */
