/* unix/ui.c 
	vi:ts=3 sw=3:
 */

/* special termio discipline for sun/sgi,
 * for non blocking io and such.
 * These functions should not be too difficult
 * to write for a PC.
 */
/* $Id: ui.c,v 4.4 1995/02/08 13:47:35 espie Exp espie $
 * $Log: ui.c,v $
 * Revision 4.4  1995/02/08  13:47:35  espie
 * *** empty log message ***
 *
 * Revision 4.3  1995/01/28  09:56:53  espie
 * Port to FreeBSD/NeXTstep.
 *
 * Revision 4.2  1995/01/28  09:23:38  espie
 * Support for old bsd io discipline (NeXT)
 *
 *
 * Revision 4.0  1994/01/11  18:02:31  espie
 * Major change: lots of new calls.
 * Stupid termio bug: shouldn't restore term to sanity if we don't
 * know what sanity is. For instance, if we haven't modified anything.
 * cflags interpreted correctly.
 * Hsavolai fix.
 * Added bg/fg test.
 */

#ifdef dec
#define stub_only
#endif

#ifdef __NeXT__
#include <libc.h>
#else /* __NeXT__ */
#if defined(linux) || defined(__FreeBSD__)
#include <termios.h>
#else
#ifdef __hpux
#include <sys/bsdtty.h>
#endif
#include <sys/termio.h>
#endif
#ifdef __FreeBSD__
#include <sys/ioctl.h>
#define termio termios  /* hack to not touch many places */
#endif
#include <signal.h>
#endif /* __NeXT__ */
#include "defs.h"
#include "extern.h"
#include "tags.h"
#include "prefs.h"


LOCAL void nonblocking_io P((void));
LOCAL void sane_tty P((void));

LOCAL void (*INIT)P((void)) = nonblocking_io;


LOCAL int show;
/* poor man's timer */
LOCAL int current_pattern;
LOCAL int count_pattern, count_song;
#define SMALL_DELAY 25

/* do not define any stdio routines if it's known not to work */

#ifdef stub_only

int run_in_fg()
   {
   return TRUE;
   }

LOCAL void sane_tty()
   {
   }

LOCAL struct tag end_marker;

struct tag *get_ui()
   {
   end_marker.type = TAG_END;
   return &end_marker;
   }

#else

#ifdef __NeXT__
LOCAL struct sgttyb sanity;
LOCAL struct sgttyb *psanity = 0;
#else
LOCAL struct termio sanity;
LOCAL struct termio *psanity = 0;
#endif

LOCAL int is_fg;

/* signal handler */

LOCAL void goodbye(sig)
int sig;
    {
    static char buffer[25];

    sprintf(buffer, "Signal %d", sig);
    end_all(buffer);
    }

LOCAL void abort_this(sig)
int sig;
   {
   end_all("Abort");
   }

#ifdef SIGTSTP
LOCAL void suspend(sig)
int sig;
   {
   fflush(stdout);
   sane_tty();
   signal(SIGTSTP, SIG_DFL);
   kill(0, SIGTSTP);
   }
#endif

int run_in_fg()
   {
   int val;

#ifdef __hpux
	if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
		return FALSE;
#endif

   /* real check for running in foreground */
   if (ioctl(fileno(stdin), TIOCGPGRP, &val))
      return FALSE;
#ifdef __POSIX_SOURCE
   if (val == getpgrp())
#else
   if (val == getpgrp(0))
#endif
      return TRUE;
   else
      return FALSE;
   }

/* if_fg_sane_tty():
 * restore tty modes, _only_ if running in foreground
 */
LOCAL void if_fg_sane_tty()
	{
	if (run_in_fg())
		sane_tty();
	}


LOCAL void switch_mode()
   {
#ifdef __NeXT__
   struct sgttyb zap;
   int tty;
#else
   struct termio zap;
#endif

#ifdef SIGTSTP
   signal(SIGTSTP, suspend);
#endif
   signal(SIGCONT, switch_mode);
   signal(SIGINT, goodbye);
   signal(SIGQUIT, goodbye);
   signal(SIGUSR1, abort_this);

   if (run_in_fg())
      {
#ifdef __NeXT__
      tty = fileno(stdin);
      fcntl(tty, F_SETFL, fcntl(tty, F_GETFL, 0) | FNDELAY);
      ioctl(tty, TIOCGETP, &zap);
      zap.sg_flags |= CBREAK;
      zap.sg_flags &= ~ECHO;
      ioctl(tty, TIOCSETP, &zap);
#else /* __NeXT__ */
#ifdef __FreeBSD__
      tcgetattr(fileno(stdin), &zap);
#else
      ioctl(fileno(stdin), TCGETA, &zap);
#endif
#ifdef linux
      zap.c_cc[VMIN] = 0;
      zap.c_cc[VTIME] = 0;
/* Commented out
As Hannu said:
The current Linux kernel interprets correctly the c_lflags field so it
should be set like for the other systems. 
      zap.c_lflag = 0;
 */
#else
#ifdef __FreeBSD__
      zap.c_cc[VMIN] = 0;     /* can't work with old */
      zap.c_cc[VTIME] = 0; /* FreeBSD versions    */
      zap.c_lflag &= ~(ICANON|ECHO|ECHONL);
#endif
      zap.c_cc[VEOL] = 0;
      zap.c_cc[VEOF] = 0;
#endif
      zap.c_lflag &= ~(ICANON | ECHO);
#ifdef __FreeBSD__
      tcsetattr(fileno(stdin), TCSADRAIN, &zap);
#else
      ioctl(fileno(stdin), TCSETA, &zap);
#endif
#endif /* __NeXT__ */
      is_fg = TRUE;
      }
   else
      is_fg = FALSE;
   }

/* nonblocking_io():
 * try to setup the keyboard to non blocking io
 */
LOCAL void nonblocking_io()
   {
   show = get_pref_scalar(PREF_SHOW);


#if 0 /* BROKEN */
   /* try to renice our own process to get more cpu time */
   if (nice(-15) == -1)
      nice(0);
#endif
   if (!psanity)
      {
      psanity = &sanity;
#ifdef __NeXT__
      ioctl(fileno(stdin), TIOCGETP, psanity);
#else /* __NeXT__ */
#ifdef __FreeBSD__
      tcgetattr(fileno(stdin), psanity);
#else
      ioctl(fileno(stdin), TCGETA, psanity);
#endif
#endif /* __NeXT__ */
      }
   switch_mode();
   at_end(if_fg_sane_tty);
   }


/* sane_tty():
 * restores everything to a sane state before returning to shell */
LOCAL void sane_tty()
   {
#ifdef __NeXT__
      ioctl(fileno(stdin), TIOCSETP, psanity);
#else /* __NeXT__ */
#ifdef __FreeBSD__
      tcsetattr(fileno(stdin), TCSADRAIN, psanity);
#else
      ioctl(fileno(stdin), TCSETA, psanity);
#endif
#endif /* __NeXT__ */
   }

LOCAL int may_getchar()
   {
   char buffer;

   INIT_ONCE;

   if (run_in_fg() && !is_fg)
      switch_mode();
   if (run_in_fg() && read(fileno(stdin), &buffer, 1))
      return buffer;
   return EOF;
   }

LOCAL struct tag result[2];

struct tag *get_ui()
   {
   result[0].type = TAG_END;
   result[1].type = TAG_END;
   count_pattern++;
   count_song++;
   switch(may_getchar())
      {
   case 'n':
      result[0].type = UI_NEXT_SONG;
      break;
   case 'p':
      if (count_song > SMALL_DELAY)
         result[0].type = UI_RESTART;
      else
         result[0].type = UI_PREVIOUS_SONG;
      count_song = 0;
      break;
   case 'x':
   case 'e':
   case 'q':
      result[0].type = UI_QUIT;
      break;
   case 's':
      result[0].type = UI_SET_BPM;
      result[0].data.scalar = 50;
      break;
   case 'S':
      result[0].type = UI_SET_BPM;
      result[0].data.scalar = 60;
      break;
   case '>':
      result[0].type = UI_JUMP_TO_PATTERN;
      result[0].data.scalar = current_pattern + 1;
      break;
   case '<':
      result[0].type = UI_JUMP_TO_PATTERN;
      result[0].data.scalar = current_pattern;
      if (count_pattern < SMALL_DELAY)
         result[0].data.scalar--;
      break;
   case '?':
      show = !show;
		set_pref_scalar(PREF_SHOW, show);
		if (show)
			putchar('\n');
      break;
   default:
      break;
      }
   return result;
   }
      
         
#endif


void notice(s)
char *s;
   {
   fprintf(stderr, "%s\n", s);
   }

void status(s)
char *s;
   {
   if (run_in_fg())
      {
		if (s)
         {
         puts(s);
         }
      else
         putchar('\n');
      }
   }

LOCAL char title[25];
void song_title(s)
char *s;
   {
	strncpy(title, s, 25);
	if (run_in_fg() && !show)
		puts(title);
   count_song = 0;
   }


LOCAL char scroll_buffer[120];

GENERIC begin_info(title)
char *title;
   {
   if (run_in_fg())
      return scroll_buffer;
   else
      return 0;
   }

void infos(handle, s)
GENERIC handle;
char *s;
   {
   if (handle)
      printf(s);
   }

void info(handle, line)
GENERIC handle;
char *line;
   {
   if (handle)
      puts(line);
   }

void end_info(handle)
GENERIC handle;
   {
   if (handle)
      fflush(stdout);
   }

LOCAL char *last_result = 0;

char *new_scroll(void)
   {
   if (run_in_fg())
      {
      last_result = scroll_buffer;
		if (get_pref_scalar(PREF_COLOR))
		  strcpy(scroll_buffer, "                    \033[0;39m\
|                    \033[0;39m\
||                    \033[0;39m\
|                    \033[0;39m");
		else
      strcpy(scroll_buffer, "             |             ||             |             ");

      }
   else
      last_result = 0;
   return last_result;
   }
   
void scroll()
   {
   if (run_in_fg() && last_result)
		{
      puts(scroll_buffer);
		fflush(stdout);
		}
   }

void display_pattern(current, total, real)
int current, total, real;
   {
   if (run_in_fg())
      {
      if (show)
         printf("\n%3d/%3d[%3d] %s\n", current, total, real, title);
      else
         printf("%3d/%3d\b\b\b\b\b\b\b", current, total);
      fflush(stdout); 
      }
   current_pattern = current;
   count_pattern = 0;
   }

int checkbrk()
   {
   return FALSE;
   }
