/*  VT1500 Device Driver for Linux, Version 1.0.5
 *  Copyright (C) 1995  M. Gutschke
 *
 *  At the time of writing my e-mail address is:
 *	Internet: gutschk@uni-muenster.de
 *  My snail mail address is:
 *	Markus Gutschke
 *	Schlage 5a
 *	48268 Greven-Gimbte
 *	Germany
 *  If you like this software, I would appreciate if you sent me a postcard
 *  from your hometown. Under the terms of the GNU general public license
 *  you are free to include this program into (commercial) software
 *  distributions (e.g. putting it onto CD-ROM); nonetheless, I would really
 *  appreciate if you dropped me a short note (sending me a sample copy of
 *  your distribution would be even more appreciated!)
 *
 *  This program 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 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <ctype.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <termios.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/unistd.h>
#include <linux/vt.h>

#include "terminal.h"
#include "osd.h"
#include "rc.h"
#include "tvcontrol.h"

int useVT = 0;
static int restoreVGA = -1;
static unsigned long myVT = -1;
static int myTTY = -1;
static int oldVT = -1;

/* VT-console switching code:
 *    shamelessly stolen from SVGALib :-)
 *    thanx to Bernd "Bernie" Meyer <root@wombat.hanse.de> for
 *    tracking down this information!
 */

#define SETSIG(sig,fun) \
        do {struct sigaction __siga; \
	__siga.sa_handler = fun; \
	__siga.sa_flags = 0; \
	__siga.sa_mask = 0; \
	sigaction(sig,&__siga,NULL); \
	} while (0)
#ifdef __GNUC__
#define UNUSED(x) ((void)(x))
#else
#define UNUSED(x)
#endif

static void releaseVT(int sig)
{
  extern void termRefresh(void);

  UNUSED(sig);
  SETSIG(SIGUSR1,releaseVT);
  if (restoreVGA < 0)
    restoreVGA = !isVisible();
  setVGA(1);
  clearScreen(); termRefresh();
  ioctl(myTTY,VT_RELDISP,1L);
  return;
} 

static void acquireVT(int sig)
{
  UNUSED(sig);
  SETSIG(SIGUSR2,acquireVT);
  clearok(curscr,1);
  setVGA(restoreVGA > 0);
  restoreVGA = -1;
  initializeTerminal();
  ioctl(myTTY,VT_RELDISP,2L);
  return;
}

static void takeVTControl(void)
{
  struct vt_mode VTmode;
  
  if (!ioctl(myTTY,VT_GETMODE,&VTmode)) {
    SETSIG(SIGUSR1,releaseVT);
    SETSIG(SIGUSR2,acquireVT);
    VTmode.mode = VT_PROCESS;
    VTmode.relsig = SIGUSR1;
    VTmode.acqsig = SIGUSR2;
    ioctl(myTTY,VT_SETMODE,&VTmode); }
  else /* This should not happen! */
    fprintf(stderr,"Warning! "
	    "Execute from VT in order to detect VT switches\n");
  return;
}

static void initSignals(int flags)
{
  static void contHandler(int signum);
  static void stopHandler(int signum);
  static void intHandler(int signum);
  static void winchHandler(int signum);

  if (flags & 1) signal(SIGTSTP,stopHandler);
  if (flags & 2) signal(SIGCONT,contHandler);
  if (flags & 4) signal(SIGINT,intHandler);
  if (flags & 8) signal(SIGTERM,intHandler);
  if (flags &16) signal(SIGWINCH,winchHandler);
  return;
}

static void changeScreenSize(void)
{
/*extern int del_curterm(TERMINAL *term);

  del_curterm(cur_term);
  setupterm(NULL,fileno(stdin),NULL);
  initializeTerminal(); */
  return;
}

void initializeTerminal(void)
{
  extern int halfdelay(int);
  static void closeTerminal(void);
  static int initialized = 0;

  if (initialized)
    endwin();
  else {
    int  err;
    if (useVT >= 0) {
      if ((myTTY = 0),(myVT = useVT) <= 0 && ioctl(myTTY,VT_OPENQRY,&myVT) &&
	  ((myTTY = open("/dev/tty0",O_RDONLY|O_NOCTTY)) < 0 ||
	   ioctl(myTTY,VT_OPENQRY,&myVT))) {
	fprintf(stderr,"Warning! Could not allocate virtual console\n");
	myVT = useVT = -1; }
      else {
	struct vt_stat vts;
	char myDevice[20];
	useVT = myVT;
	sprintf(myDevice,"/dev/tty%ld",myVT);
	if (myTTY >= 0) {
	  int tty = open(myDevice,O_RDWR);
	  if (tty >= 0) {
	    close(myTTY);
	    myTTY = tty; } }
	if (ioctl(myTTY,VT_GETSTATE,&vts) == 0)
	  oldVT = vts.v_active;
	ioctl(myTTY,VT_WAITACTIVE,myVT); /* do we want this ? */
	ioctl(myTTY,VT_ACTIVATE,myVT);
	ioctl(myTTY,VT_WAITACTIVE,myVT);
	if (freopen(myDevice,"a+",stdin) == NULL) {
	  fprintf(stderr,"Could not redirect stdin: %s\n",strerror(errno));
	  exit(1); }
	if (freopen(myDevice,"a+",stdout) == NULL) {
	  fprintf(stderr,"Could not redirect stdout: %s\n",strerror(errno));
	  exit(1); } } }
    if (getenv("TERM") == NULL) {
      fprintf(stderr,"Environment variable TERM not set; assuming `vt100'\n");
      putenv("TERM=vt100"); }
    if (setupterm(NULL,fileno(stdin),&err) < 1) {
      fprintf(stderr,"Environment variable TERM is not valid; resetting to "
	             "`vt100'\n");
      putenv("TERM=vt100"); }
    if (initscr() == NULL) {
      fprintf(stderr,"Cannot open curses screen\n");
      exit(1); }
    atexit(closeTerminal);
    if (myVT > 0)
      takeVTControl();
    initialized = 1; }
  curs_set(0);
  leaveok(stdscr,1);
  cbreak();
  noecho();
  nonl();
  clearok(curscr,1);
  meta(stdscr,1);
  keypad(stdscr,1);
  scrollok(stdscr,0);
  refresh();
  halfdelay(50);
  initSignals(~0);
  return;
}

static void closeTerminal(void)
{
  saveSettings();
  initSignals(~0);
  if (myVT > 0) {
    ioctl(myTTY,VT_ACTIVATE,oldVT > 0 ? oldVT : 1);
    ioctl(myTTY,VT_WAITACTIVE,oldVT > 0 ? oldVT : 1); }
  clearok(curscr,1);
  refresh();
  endwin();
  return;
}

static void stopHandler(int signum)
{
  UNUSED(signum);
  endwin();
  if (restoreVGA < 0)
    restoreVGA = !isVisible();
  setVGA(1);
  initSignals(~1);
  raise(SIGTSTP);
  return;
}

static void contHandler(int signum)
{
  extern void termRefresh(void);

  UNUSED(signum);
  clearok(curscr,1);
  initializeTerminal();
  if (myVT > 0)
    ioctl(myTTY,VT_ACTIVATE,myVT);
  setVGA(restoreVGA > 0);
  restoreVGA = -1;
  clearScreen(); termRefresh();
  initSignals(~0);
  return;
}

static void intHandler(int signum)
{
  UNUSED(signum);
  exit(1);
}

static void winchHandler(int signum)
{
  UNUSED(signum);
  changeScreenSize();
  initSignals(~0);
  return;
}

int getKey(void)
{
  extern void checkTvTimeout(void);
  int ch = getch();

  initSignals(~0);
  checkTvTimeout();
  return(ch >= 0 && ch < 256 ? toupper(ch) : ch);
}
