static char rcsid[] = "$Id: stio.c,v 1.9 1992/11/29 01:14:50 mike Exp $";

/* $Log: stio.c,v $
 * Revision 1.9  1992/11/29  01:14:50  mike
 * - Set the default text height (10 pt) using `vst_point'.
 *
 * Revision 1.8  1992/11/10  02:21:06  mike
 * - Some clean ups to prevent warnings from gcc.
 *
 * Revision 1.7  1992/11/08  23:28:02  mike
 * - Show the cursor during `wait_for_key'.
 *
 * Revision 1.6  1992/10/28  15:12:14  mike
 * - Replaced `toggle_cursor()' by `Cursor_on()' and `Cursor_off()'.
 * - Fixed bug in the keyboard driver.
 *
 * Revision 1.5  1992/10/28  00:27:44  mike
 * - Added support for the buffers window.
 *
 * Revision 1.4  1992/10/12  22:26:04  mike
 * - Discarded virtual workstation `Vw_clear'. It's job is done by
 *   `Vw_print' now.
 *
 * Revision 1.3  1992/10/02  22:31:40  mike
 * - Some changes for the Atari ST/TT version.
 *
 * Revision 1.2  1992/09/14  18:03:42  mike
 * - First set of changes for the Atari ST/TT version.
 *
 * Revision 1.1  1992/09/05  01:13:32  mike
 * Initial revision
 *
 */

/*
 *		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
 *
 * GEM stuff by Michael Brandt, mike@pc-labor.uni-bremen.de
 *
 * Public Domain
 */

#include <stdio.h>
#include <sysvars.h>

#include "me2.h"
#include "term.h"
#include "config.h"

/*
 *	Codes for special keys for use with Kbshift()
 */

#define KBD_SHIFT	0x0003
#define KBD_CTRL	0x0004
#define KBD_ALT		0X0008

#define BOGUS	(PFIX2 | '@')	/* Code for bogus key combination	*/

/***************************************************************************/
/*****	The top section of this file is for TOS, the bottom for MiNT *******/
/***************************************************************************/

#ifndef __MINT__
/***************************************************************************/
/*****				TOS section			     *******/
/***************************************************************************/

#include <aesbind.h>
#include <gemdefs.h>
#include <osbind.h>
#include <vdibind.h>

#include "gem.h"

static char *Keytable;		/* Keytable to convert ALT keys	*/

/* ******************************************************************** */
/* ******** Opening and closing the terminal ************************** */
/* ******************************************************************** */


/*
 *	Initialize the menu buffer.
 */

static void init_menu_buffer()
{
  register long  i;
  register long *buffer;
  register long *ptr;

  buffer = Menu_buffer = (long *) malloc(Menu_buffer_size << 2);
  i      = Menu_buffer_size;
  ptr    = (long *) Physbase();
  while (i--)
    *buffer++ = *ptr++;
}



/*
 * This function is called to set up the terminal device streams.
 */

void ttopen()
{
  extern void top_text_window();
  extern int  t_ncol,t_nrow;
  static char title[32];
  static int  work_in[11] = {1,1,1,1,1,1,1,1,1,1,2};
  static int  work_out[57];
         int  x,y,w,h;

  if (Text_window < 0)
  {
    menu_bar(Menu,1);
    Vw_print    = Phys_handle;
    Vw_cursor   = Phys_handle;
    v_opnvwk(work_in,&Vw_print,work_out);
    v_opnvwk(work_in,&Vw_cursor,work_out);
    v_opnvwk(work_in,&Bw.vw_text,work_out);
    if (!Vw_print || !Vw_cursor || !Bw.vw_text)
    {
      form_alert(1,
	"[3][PANIC: Cannot open|virtual screen workstation!][Abort]");
      exit(1);
    }
    graf_mouse(ARROW,NULL);
    graf_mouse(M_OFF,NULL);
    vsf_interior(Vw_print,0);			/* Empty		*/
    vsf_perimeter(Vw_print,0);			/* No frame		*/
    vsf_color(Vw_print,0);			/* Bg color		*/
    vst_color(Vw_print,1);			/* Text in "black"	*/
    vst_alignment(Vw_print,0,3,&x,&x);
    vst_point(Vw_print,Font_size,&x,&x,&Wchar,&Hchar);
    vswr_mode(Vw_cursor,3);			/* XOR writing mode	*/
    if (!t_ncol)
    {
      Keytable = ((char **) Keytbl(-1L,-1L,-1L))[2];
      Wkind = NAME | MOVER | FULLER;
      wind_get(0,WF_WORKXYWH,&Deskx,&Desky,&Deskw,&Deskh);
      wind_calc(WC_WORK,Wkind,Deskx,Desky,Deskw,Deskh,&Fullx,&Fully,
	        &Fullw,&Fullh);
      if (load_setup())
      {
	t_ncol = Workw / Wchar;
	t_nrow = Workh / Hchar - 1;
	Workw  = t_ncol * Wchar;
	Workh  = (t_nrow + 1) * Hchar;
      }
      else
      {
        t_ncol   = Fullw / Wchar;
        t_nrow   = Fullh / Hchar - 1;
        if (t_ncol > 80)
	  t_ncol = 80;
        Workw    = t_ncol * Wchar;
        Workh    = (t_nrow + 1) * Hchar;
        Workx    = Fullx + ((Fullw - Workw) >> 1);
        Worky    = Fully;
      }
      Cursor_x  = Workx;
      Cursor_y  = Worky + Hchar - 1;
      Menu_buffer_size = (work_out[0] + 1) >> 5;
      vq_extnd(Vw_print,1,work_out);
      Menu_buffer_size *= work_out[4] * Desky;
      init_menu_buffer();
    }
    if ((Text_window = wind_create(Wkind,Deskx,Desky,Deskw,Deskh)) < 0)
    {
      form_alert(1,"[3][PANIC: Cannot get a window!][Abort]");
      exit(1);
    }
    sprintf(title," Mutt Editor 2 (%dx%d) ", t_ncol, t_nrow + 1);
    wind_set(Text_window,WF_NAME,title);
    wind_calc(WC_BORDER,Wkind,Workx,Worky,Workw,Workh,&x,&y,&w,&h);
    wind_open(Text_window,x,y,w,h);
  }
  if (Buffers_window > 0)
  {
    Buffers_window = 0;
    open_buffers_window();
    top_text_window();
  }
}



/*
 * This function is called to close the terminal device streams.
 */

void ttclose()
{
  if (Text_window >= 0)
  {
    menu_bar(Menu,0);
    wind_close(Text_window);
    wind_delete(Text_window);
    Text_window = -1;
    v_clsvwk(Vw_print);
    v_clsvwk(Vw_cursor);
    v_clsvwk(Bw.vw_text);
    graf_mouse(M_ON,NULL);
  }
  if (Buffers_window > 0)
  {
    close_buffers_window();
    Buffers_window = 1;
  }
}



/*
 *	Toggle the state of the cursor and the mouse pointer.
 */

       short Cursor_visible = 0;
static int   Cursor_array[4];

void Cursor_on()
{
  if (!Cursor_visible)
  {
    Cursor_array[0] = Cursor_x - 1;
    Cursor_array[1] = Cursor_y + 1;
    Cursor_array[2] = Cursor_x + Wchar;
    Cursor_array[3] = Cursor_y - Hchar;
    v_bar(Vw_cursor,Cursor_array);
    graf_mouse(M_ON,NULL);
    Cursor_visible = 1;
  }
}

void Cursor_off()
{
  if (Cursor_visible)
  {
    graf_mouse(M_OFF,NULL);
    v_bar(Vw_cursor,Cursor_array);
    Cursor_visible = 0;
  }
}



/* ******************************************************************** */
/* ************** Terminal I/O  *************************************** */
/* ******************************************************************** */



/*
 * Write a character to the display.
 * Use the rawest console output routine that handles backspace, \r and \n.
 */

void t_putchar(c)
int c;
{
  static char text[2] = { '\0', '\0' };

  switch (c)
  {
    case '\010' :
      if (Cursor_x - Wchar >= Workx)
	Cursor_x -= Wchar;
      break;
    case '\n' :
      if (Cursor_y + Hchar < Worky + Workh)
	Cursor_y += Hchar;
      break;
    case '\r' :
      Cursor_x = Workx;
      break;
    default :
      if (Cursor_x + Wchar < Workx + Workw)
      {
	text[0] = c;
	v_gtext(Vw_print,Cursor_x,Cursor_y,text);
	Cursor_x += Wchar;
      }
  }
}



void t_putstr(p1,p2)
register char *p1,*p2;
{
  char c;

  c   = *p2;
  *p2 = '\0';
  v_gtext(Vw_print,Cursor_x,Cursor_y,p1);
  Cursor_x += (p2 - p1) * Wchar;
  *p2 = c;
}



/*
 * Flush terminal buffer. Does real work when the terminal output is
 *   buffered up.
 */
void 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_getcharX(key)
int key;
{
  extern   void           t_beep();
  register unsigned short sc;		/* Scancode		*/
  register unsigned short ascii;	/* ASCII code		*/
  register KeyCode        keycode;	/* Keycode		*/
           unsigned short sk;		/* Value from Kbshift()	*/

  if (!key)
    key = evnt_keybd();
  sk    = Kbshift(-1);
  sc    = (key >> 8) & 0xff;
  ascii = key & 0xff;
  if (ascii)		/* Everything that returns a character */
  {
    switch ((sc << 8) | ascii)
    {
      case 0x4b34 :           /* Shift cursor left	*/
	return SOFKEY | 'Q';
      case 0x4d36 :           /* Shift cursor right	*/
	return SOFKEY | 'R';
      case 0x5032 :           /* Shift cursor down	*/
	return SOFKEY | 'J';
      case 0x4838 :           /* Shift cursor up	*/
	return SOFKEY | 'I';
      case 0x5230 :           /* Shift insert		*/
	return SOFKEY | 'M';
      case 0x4737 :           /* Clr			*/
	return SOFKEY | 'B';
      case 0x537f :		/* Del			*/
	if (sk & KBD_SHIFT)
	  return SOFKEY | 'L';
	else
	  return SOFKEY | 'H';
    }
    return ascii;
  }
  else                  /* All non character keys */
  {
    switch (sc)
    {
      case 0x4b :	      /* Cursor left */
	return SOFKEY | 'F';
      case 0x73 :	      /* Ctrl cursor left */
	return SOFKEY | 'S';
      case 0x4d :	      /* Cursor right */
	return SOFKEY | 'E';
      case 0x74 :	      /* Ctrl cursor right */
	return SOFKEY | 'T';
      case 0x50 :
	if (sk & KBD_CTRL)      /* Ctrl cursor down */
	  return SOFKEY | 'O';
	else		      /* Cursor down */
	  return SOFKEY | 'D';
      case 0x48 :
	if (sk & KBD_CTRL)      /* Ctrl cursor up */
	  return SOFKEY | 'P';
	else		      /* Cursor up */
	  return SOFKEY | 'C';
      case 0x52 :	      /* Insert */
	return SOFKEY | 'G';
      case 0x47 :	      /* Home */
	return SOFKEY | 'A';
#if 0
      case 0x77 :	      /* Ctrl home */
      {
	keycode = BOGUS;
	goto done;
      }
#endif
      case 0x62 :
	if (sk & KBD_CTRL)	/* Ctrl Help */
	  return SOFKEY | 'U';
	else
	  return SOFKEY | 'K';	/* Help */
      case 0x61 :		/* Undo */
	return SOFKEY | 'N';
    }
    if (sk & KBD_ALT)
      keycode = PFIX2;
    else
      keycode = 0;
    if (sk & KBD_CTRL)
      keycode |= CTRL;
    if (sc >= 0x3b && sc <= 0x43)
      /*
       *	F1 to F9
       */
      return keycode | SOFKEY | (sc - 0x3b + '1');
    else if (sc >= 0x54 && sc <= 0x5c)
      /*
       *	Shift F1 to Shift F9
       */
      return keycode | SOFKEY | SHIFT | (sc - 0x54 + '1');
    else if (sc == 0x44)
      /*
       *	F10
       */
      return keycode | SOFKEY | '0';
    else if (sc == 0x5d)
      /*
       *	Shift F10
       */
      return keycode | SOFKEY | SHIFT | '0';
    else if (sc >= 120 && sc <= 128)
      /*
       *	ALT-1 to ALT-9
       */
      return keycode | PFIX2 | (sc - 120 + '1');
    else if (sc == 129)
      /*
       *	ALT-0
       */
      return keycode | PFIX2 | '0';
  }
  if (sk & KBD_ALT)
    return PFIX2 | Keytable[sc];
  t_beep();
  return BOGUS;		/* Bogus key combination */
}



KeyCode t_getchar()
{
  KeyCode key;

  Cursor_on();
  key = t_getcharX(0);
  Cursor_off();
  return key;
}


    /* 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;
{
#if 0
  extern   int   atoi();
  extern   char *getenv();
  register char *p;

  if ((p = getenv("ROWS")) == NULL)
    return FALSE;
  *row = atoi(p);
  if ((p = getenv("COLUMNS")) == NULL)
    return FALSE;
  *col = atoi(p);
  return TRUE;
#else
  return FALSE;
#endif
}



/* ******************************************************************** */
/* *********** 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.
 */

int wait_for_key(sec)
int sec;
{
  register long waitUntil;

  if (sec)
  {
    waitUntil = get_sysvar(_hz_200) + (((long) sec) << 8);
    if (!Cursor_visible)
    {
      Cursor_array[0] = Cursor_x - 1;
      Cursor_array[1] = Cursor_y + 1;
      Cursor_array[2] = Cursor_x + Wchar;
      Cursor_array[3] = Cursor_y - Hchar;
      v_bar(Vw_cursor,Cursor_array);
    }
    do
    {
      if (Bconstat(2))
      {
	if (!Cursor_visible)
	  v_bar(Vw_cursor,Cursor_array);
	return TRUE;
      }
    }
    while (get_sysvar(_hz_200) < waitUntil);
    if (!Cursor_visible)
      v_bar(Vw_cursor,Cursor_array);
    return FALSE;
  }
  return Bconstat(2);
}



/*
 *	keywaiting:
 *	Check to see if any characters are already in the keyboard buffer.
 */

int 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 */
