
/*
   ** This module contains functions to support i/o in the TUI
 */


#include <stdio.h>
#include "defs.h"
#include "terminal.h"
#include "tui.h"
#include "tuiData.h"
#include "tuiIO.h"
#include "tuiCommand.h"
#include "tuiWin.h"

#ifdef ANSI_PROTOTYPES
#include <stdarg.h>
#else
#include <varargs.h>
#endif

/* The Solaris header files seem to provide no declaration for this at
   all when __STDC__ is defined.  This shouldn't conflict with
   anything.  */
extern char *tgoto ();

int insert_mode = 0;

/********************************************
**       LOCAL STATIC FORWARD DECLS        **
********************************************/
static void _updateCommandInfo (int);
static unsigned int _tuiHandleResizeDuringIO (unsigned int);


/*********************************************************************************
**                              PUBLIC FUNCTIONS                                **
*********************************************************************************/

/*
   ** tuiPuts_unfiltered().
   **        Function to put a string to the command window
   **              When running in TUI mode, this is the "hook"
   **              for fputs_unfiltered(). That is, all debugger
   **              output eventually makes it's way to the bottom-level
   **              routine fputs_unfiltered (main.c), which (in TUI
   **              mode), calls tuiPuts_unfiltered().
 */
void
#ifdef __STDC__
tuiPuts_unfiltered (
		     const char *string,
		     struct ui_file * stream)
#else
tuiPuts_unfiltered (string, stream)
     char *string;
     struct ui_file *stream;
#endif
{
  int len = strlen (string);
  int i, linech;

  for (i = 0; i < len; i++)
    {
      if (string[i] == '\n' || string[i] == '\r')
	m_tuiStartNewLine;
      else
	{
	  if ((cmdWin->detail.commandInfo.curch + 1) > cmdWin->generic.width)
	    m_tuiStartNewLine;

	  if (insert_mode)
	    {
	      mvwinsch (cmdWin->generic.handle,
			cmdWin->detail.commandInfo.curLine,
			cmdWin->detail.commandInfo.curch++,
			string[i]);
	      wmove (cmdWin->generic.handle,
		     cmdWin->detail.commandInfo.curLine,
		     cmdWin->detail.commandInfo.curch);
	    }
	  else
	    mvwaddch (cmdWin->generic.handle,
		      cmdWin->detail.commandInfo.curLine,
		      cmdWin->detail.commandInfo.curch++,
		      string[i]);
	}
    }
  tuiRefreshWin (&cmdWin->generic);

  return;
}				/* tuiPuts_unfiltered */

/* A cover routine for tputs().
 * tputs() is called from the readline package to put
 * out strings representing cursor positioning.
 * In TUI mode (non-XDB-style), tui_tputs() is called instead.
 *
 * The reason we need to hook tputs() is:
 * Since the output is going to curses and not to
 * a raw terminal, we need to intercept these special
 * sequences, and handle them them here.
 *
 * This function seems to be correctly handling all sequences
 * aimed at hpterm's, but there is additional work to do
 * for xterm's and dtterm's. I abandoned further work on this
 * in favor of "XDB style". In "XDB style", the command region
 * looks like terminal, not a curses window, and this routine
 * is not called. - RT
 */
void
tui_tputs (str, affcnt, putfunc)
     char *str;
     int affcnt;
     int (*putfunc) (int);
{
  extern char *rl_prompt;	/* the prompt string */

  /* This set of globals are defined and initialized
   * by the readline package.
   *
   * Note we're assuming tui_tputs() is being called
   * by the readline package. That's because we're recognizing
   * that a given string is being passed by
   * matching the string address against readline's
   * term_<whatever> global. To make this more general,
   * we'd have to actually recognize the termcap sequence
   * inside the string (more work than I want to do). - RT
   *
   * We don't see or need to handle every one of these here;
   * this is just the full list defined in readline/readline.c
   */
  extern char *term_backspace;
  extern char *term_clreol;
  extern char *term_clrpag;
  extern char *term_cr;
  extern char *term_dc;
  extern char *term_ei;
  extern char *term_goto;
  extern char *term_ic;
  extern char *term_im;
  extern char *term_mm;
  extern char *term_mo;
  extern char *term_up;
  extern char *term_scroll_region;
  extern char *term_memory_lock;
  extern char *term_memory_unlock;
  extern char *term_cursor_move;
  extern char *visible_bell;

  /* Sanity check - if not TUI, just call tputs() */
  if (!tui_version)
    tputs (str, affcnt, putfunc);

  /* The strings we special-case are handled first */

  if (str == term_backspace)
    {
      /* Backspace. */

      /* We see this on an emacs control-B.
         * I.e., it's like the left-arrow key (not like the backspace key).
         * The effect that readline wants when it transmits this
         * character to us is simply to back up one character
         * (but not to write a space over the old character).
       */

      _updateCommandInfo (-1);
      wmove (cmdWin->generic.handle,
	     cmdWin->detail.commandInfo.curLine,
	     cmdWin->detail.commandInfo.curch);
      wrefresh (cmdWin->generic.handle);

    }
  else if (str == term_clreol)
    {

      /* Clear to end of line. */
      wclrtoeol (cmdWin->generic.handle);
      wrefresh (cmdWin->generic.handle);

    }
  else if (str == term_cr)
    {

      /* Carriage return */
      _updateCommandInfo (-cmdWin->detail.commandInfo.curch);
      wmove (cmdWin->generic.handle,
	     cmdWin->detail.commandInfo.curLine,
	     0 /* readline will rewrite the prompt from 0 */ );
      wrefresh (cmdWin->generic.handle);

    }
  else if (str == term_goto)
    {

      /* This is actually a tgoto() specifying a character position,
         * followed by either a term_IC/term_DC which [I think] means
         * insert/delete one character at that position.
         * There are complications with this one - need to either
         * extract the position from the string, or have a backdoor
         * means of communicating it from ../readline/display.c.
         * So this one is not yet implemented.
         * Not doing it seems to have no ill effects on command-line-editing
         * that I've noticed so far. - RT
       */

    }
  else if (str == term_dc)
    {

      /* Delete character at current cursor position */
      wdelch (cmdWin->generic.handle);
      wrefresh (cmdWin->generic.handle);

    }
  else if (str == term_im)
    {

      /* Turn on insert mode. */
      insert_mode = 1;

    }
  else if (str == term_ei)
    {

      /* Turn off insert mode. */
      insert_mode = 0;

      /* Strings we know about but don't handle
         * specially here are just passed along to tputs().
         *
         * These are not handled because (as far as I can tell)
         * they are not actually emitted by the readline package
         * in the course of doing command-line editing. Some of them
         * theoretically could be used in the future, in which case we'd
         * need to handle them.
       */
    }
  else if (str == term_ic ||	/* insert character */
	   str == term_cursor_move ||	/* cursor move */
	   str == term_clrpag ||	/* clear page */
	   str == term_mm ||	/* turn on meta key */
	   str == term_mo ||	/* turn off meta key */
	   str == term_up ||	/* up one line (not expected) */
	   str == term_scroll_region ||		/* set scroll region */
	   str == term_memory_lock ||	/* lock screen above cursor */
	   str == term_memory_unlock ||		/* unlock screen above cursor */
	   str == visible_bell)
    {				/* flash screen */
      tputs (str, affcnt, putfunc);
    }
  else
    {				/* something else */
      tputs (str, affcnt, putfunc);
    }
}				/* tui_tputs */


/*
   ** tui_vwgetch()
   **        Wrapper around wgetch with the window in a va_list
 */
unsigned int
#ifdef __STDC__
tui_vwgetch (va_list args)
#else
tui_vwgetch (args)
     va_list args;
#endif
{
  unsigned int ch;
  WINDOW *window;

  window = va_arg (args, WINDOW *);

  return ((unsigned int) wgetch (window));
}				/* tui_vwgetch */


/*
   ** tui_vread()
   **   Wrapper around read() with paramets in a va_list
 */
unsigned int
#ifdef __STDC__
tui_vread (va_list args)
#else
tui_vread (args)
     va_list args;
#endif
{
  int result = 0;
  int filedes = va_arg (args, int);
  char *buf = va_arg (args, char *);
  int nbytes = va_arg (args, int);

  result = read (filedes, buf, nbytes);

  return result;
}				/* tui_vread() */

/*
   ** tuiRead()
   **    Function to perform a read() catching resize events
 */
int
#ifdef __STDC__
tuiRead (
	  int filedes,
	  char *buf,
	  int nbytes)
#else
tuiRead (filedes, buf, nbytes)
     int filedes;
     char *buf;
     int nbytes;
#endif
{
  int result = 0;

  result = (int) vcatch_errors ((OpaqueFuncPtr) tui_vread, filedes, buf, nbytes);
  *buf = _tuiHandleResizeDuringIO (*buf);

  return result;
}				/* tuiRead */


/*
   ** tuiGetc().
   **        Get a character from the command window.
   **           This is called from the readline package,
   **              that is, we have:
   **                tuiGetc() [here], called from
   **                readline code [in ../readline/], called from
   **                command_line_input() in top.c
 */
unsigned int
#ifdef __STDC__
tuiGetc (void)
#else
tuiGetc ()
#endif
{
  unsigned int ch;
  extern char *rl_prompt;
  extern char *rl_line_buffer;
  extern int rl_point;

  /* Call the curses routine that reads one character */
#ifndef COMMENT
  ch = (unsigned int) vcatch_errors ((OpaqueFuncPtr) tui_vwgetch,
				     cmdWin->generic.handle);
#else
  ch = wgetch (cmdWin->generic.handle);
#endif
  ch = _tuiHandleResizeDuringIO (ch);

  if (m_isCommandChar (ch))
    {				/* Handle prev/next/up/down here */
      tuiTermSetup (0);
      ch = tuiDispatchCtrlChar (ch);
      cmdWin->detail.commandInfo.curch = strlen (rl_prompt) + rl_point;
      tuiTermUnsetup (0, cmdWin->detail.commandInfo.curch);
    }
  if (ch == '\n' || ch == '\r' || ch == '\f')
    cmdWin->detail.commandInfo.curch = 0;
  else
    tuiIncrCommandCharCountBy (1);

  return ch;
}				/* tuiGetc */


/*
   ** tuiBufferGetc().
 */
/*elz: this function reads a line of input from the user and
   puts it in a static buffer. Subsequent calls to this same function
   obtain one char at the time, providing the caller with a behavior
   similar to fgetc. When the input is buffered, the backspaces have
   the needed effect, i.e. ignore the last char active in the buffer */
/* so far this function is called only from the query function in
   utils.c */

unsigned int
#ifdef __STDC__
tuiBufferGetc (void)
#else
tuiBufferGetc ()
#endif
{
  unsigned int ch;
  static unsigned char _ibuffer[512];
  static int index_read = -1;
  static int length_of_answer = -1;
  int pos = 0;

  if (length_of_answer == -1)
    {
      /* this is the first time through, need to read the answer */
      do
	{
	  /* Call the curses routine that reads one character */
	  ch = (unsigned int) wgetch (cmdWin->generic.handle);
	  if (ch != '\b')
	    {
	      _ibuffer[pos] = ch;
	      pos++;
	    }
	  else
	    pos--;
	}
      while (ch != '\r' && ch != '\n');

      length_of_answer = pos;
      index_read = 0;
    }

  ch = _ibuffer[index_read];
  index_read++;

  if (index_read == length_of_answer)
    {
      /*this is the last time through, reset for next query */
      index_read = -1;
      length_of_answer = -1;
    }

  wrefresh (cmdWin->generic.handle);

  return (ch);
}				/* tuiBufferGetc */


/*
   ** tuiStartNewLines().
 */
void
#ifdef __STDC__
tuiStartNewLines (
		   int numLines)
#else
tuiStartNewLines (numLines)
     int numLines;
#endif
{
  if (numLines > 0)
    {
      if (cmdWin->generic.viewportHeight > 1 &&
	cmdWin->detail.commandInfo.curLine < cmdWin->generic.viewportHeight)
	cmdWin->detail.commandInfo.curLine += numLines;
      else
	scroll (cmdWin->generic.handle);
      cmdWin->detail.commandInfo.curch = 0;
      wmove (cmdWin->generic.handle,
	     cmdWin->detail.commandInfo.curLine,
	     cmdWin->detail.commandInfo.curch);
      tuiRefreshWin (&cmdWin->generic);
    }

  return;
}				/* tuiStartNewLines */


/*
   ** tui_vStartNewLines().
   **        With numLines in a va_list
 */
void
#ifdef __STDC__
tui_vStartNewLines (
		     va_list args)
#else
tui_vStartNewLines (args)
     va_list args;
#endif
{
  int numLines = va_arg (args, int);

  tuiStartNewLines (numLines);

  return;
}				/* tui_vStartNewLines */


/****************************************************************************
**                   LOCAL STATIC FUNCTIONS                                **
*****************************************************************************/


/*
   ** _tuiHandleResizeDuringIO
   **    This function manages the cleanup when a resize has occured
   **    From within a call to getch() or read.  Returns the character
   **    to return from getc or read.
 */
static unsigned int
#ifdef __STDC__
_tuiHandleResizeDuringIO (
			   unsigned int originalCh)	/* the char just read */
#else
_tuiHandleResizeDuringIO (originalCh)
     unsigned int originalCh;
#endif
{
  if (tuiWinResized ())
    {
      tuiDo ((TuiOpaqueFuncPtr) tuiRefreshAll);
      dont_repeat ();
      tuiSetWinResizedTo (FALSE);
      rl_reset ();
      return '\n';
    }
  else
    return originalCh;
}				/* _tuiHandleResizeDuringIO */


/*
   ** _updateCommandInfo().
   **        Function to update the command window information.
 */
static void
#ifdef __STDC__
_updateCommandInfo (
		     int sizeOfString)
#else
_updateCommandInfo (sizeOfString)
     int sizeOfString;
#endif
{

  if ((sizeOfString +
       cmdWin->detail.commandInfo.curch) > cmdWin->generic.width)
    {
      int newCurch = sizeOfString + cmdWin->detail.commandInfo.curch;

      tuiStartNewLines (1);
      cmdWin->detail.commandInfo.curch = newCurch - cmdWin->generic.width;
    }
  else
    cmdWin->detail.commandInfo.curch += sizeOfString;

  return;
}				/* _updateCommandInfo */


/* Looked at in main.c, fputs_unfiltered(), to decide
 * if it's safe to do standard output to the command window.
 */
int tui_owns_terminal = 0;

/* Called to set up the terminal for TUI (curses) I/O.
 * We do this either on our way "in" to GDB after target
 * program execution, or else within tuiDo just before
 * going off to TUI routines.
 */

void
#ifdef __STDC__
tuiTermSetup (
	       int turn_off_echo)
#else
tuiTermSetup (turn_off_echo)
     int turn_off_echo;
#endif
{
  char *buffer;
  int start;
  int end;
  int endcol;
  extern char *term_scroll_region;
  extern char *term_cursor_move;
  extern char *term_memory_lock;
  extern char *term_memory_unlock;

  /* Turn off echoing, since the TUI does not
     * expect echoing. Below I only put in the TERMIOS
     * case, since that is what applies on HP-UX. turn_off_echo
     * is 1 except for the case where we're being called
     * on a "quit", in which case we want to leave echo on.
   */
  if (turn_off_echo)
    {
#ifdef HAVE_TERMIOS
      struct termios tio;
      tcgetattr (0, &tio);
      tio.c_lflag &= ~(ECHO);
      tcsetattr (0, TCSANOW, &tio);
#endif
    }

  /* Compute the start and end lines of the command
     * region. (Actually we only use end here)
   */
  start = winList[CMD_WIN]->generic.origin.y;
  end = start + winList[CMD_WIN]->generic.height - 1;
  endcol = winList[CMD_WIN]->generic.width - 1;

  if (term_memory_unlock)
    {

      /* Un-do the effect of the memory lock in terminal_inferior() */
      tputs (term_memory_unlock, 1, (int (*) (int)) putchar);
      fflush (stdout);

    }
  else if (term_scroll_region)
    {

      /* Un-do the effect of setting scroll region in terminal_inferior() */
      /* I'm actually not sure how to do this (we don't know for
       * sure what the scroll region was *before* we changed it),
       * but I'll guess that setting it to the whole screen is
       * the right thing. So, ...
       */

      /* Set scroll region to be 0..end */
      buffer = (char *) tgoto (term_scroll_region, end, 0);
      tputs (buffer, 1, (int (*) (int)) putchar);

    }				/* else we're out of luck */

  /* This is an attempt to keep the logical & physical
     * cursor in synch, going into curses. Without this,
     * curses seems to be confused by the fact that
     * GDB has physically moved the curser on it. One
     * visible effect of removing this code is that the
     * locator window fails to get updated and the line
     * of text that *should* go into the locator window
     * often goes to the wrong place.
   */
  /* What's done here is to  tell curses to write a ' '
     * at the bottom right corner of the screen.
     * The idea is to wind up with the cursor in a known
     * place.
     * Note I'm relying on refresh()
     * only writing what changed (the space),
     * not the whole screen.
   */
  standend ();
  move (end, endcol - 1);
  addch (' ');
  refresh ();

  tui_owns_terminal = 1;
}				/* tuiTermSetup */


/* Called to set up the terminal for target program I/O, meaning I/O
 * is confined to the command-window area.  We also call this on our
 * way out of tuiDo, thus setting up the terminal this way for
 * debugger command I/O.  */
void
#ifdef __STDC__
tuiTermUnsetup (
		 int turn_on_echo,
		 int to_column)
#else
tuiTermUnsetup (turn_on_echo, to_column)
     int turn_on_echo;
     int to_column;
#endif
{
  int start;
  int end;
  int curline;
  char *buffer;
  /* The next bunch of things are from readline */
  extern char *term_scroll_region;
  extern char *term_cursor_move;
  extern char *term_memory_lock;
  extern char *term_memory_unlock;
  extern char *term_se;

  /* We need to turn on echoing, since the TUI turns it off */
  /* Below I only put in the TERMIOS case, since that
     * is what applies on HP-UX.
   */
  if (turn_on_echo)
    {
#ifdef HAVE_TERMIOS
      struct termios tio;
      tcgetattr (0, &tio);
      tio.c_lflag |= (ECHO);
      tcsetattr (0, TCSANOW, &tio);
#endif
    }

  /* Compute the start and end lines of the command
     * region, as well as the last "real" line of
     * the region (normally same as end, except when
     * we're first populating the region)
   */
  start = winList[CMD_WIN]->generic.origin.y;
  end = start + winList[CMD_WIN]->generic.height - 1;
  curline = start + winList[CMD_WIN]->detail.commandInfo.curLine;

  /* We want to confine target I/O to the command region.
     * In order to do so, we must either have "memory lock"
     * (hpterm's) or "scroll regions" (xterm's).
   */
  if (term_cursor_move && term_memory_lock)
    {

      /* Memory lock means lock region above cursor.
       * So first position the cursor, then call memory lock.
       */
      buffer = tgoto (term_cursor_move, 0, start);
      tputs (buffer, 1, (int (*) (int)) putchar);
      tputs (term_memory_lock, 1, (int (*) (int)) putchar);

    }
  else if (term_scroll_region)
    {

      /* Set the scroll region to the command window */
      buffer = tgoto (term_scroll_region, end, start);
      tputs (buffer, 1, (int (*) (int)) putchar);

    }				/* else we can't do anything about target I/O */

  /* Also turn off standout mode, in case it is on */
  if (term_se != NULL)
    tputs (term_se, 1, (int (*) (int)) putchar);

  if (term_cursor_move) {
    /* Now go to the appropriate spot on the end line */
    buffer = tgoto (term_cursor_move, to_column, end);
    tputs (buffer, 1, (int (*) (int)) putchar);
    fflush (stdout);
  }

  tui_owns_terminal = 0;
}				/* tuiTermUnsetup */
