/* vi: set sw=4 ts=4: */
/*
 * KON2 - Kanji ON Console -
 * Copyright (C) 1992-1996 Takashi MANABE (manabe@papilio.tutics.tut.ac.jp)
 *
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * 
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<unistd.h>
#include	<string.h>
#include	<signal.h>
#include	<errno.h>
#include	<fcntl.h>
#include	<time.h>

#include	"getcap.h"
#include	"defs.h"
#include	"errors.h"
#include	"vc.h"
#include	"child.h"
#include	"mouse.h"
#include 	"key.h"
#include	"vga.h"
#include	"hzinput.h"

#ifdef HAVE_PWD_H
#include    <pwd.h>
#endif

#ifdef HAVE_SYS_IOCTL_H
#include	<sys/ioctl.h>
#endif

#ifdef HAVE_SYS_WAIT_H
#include	<sys/wait.h>
#endif

#ifdef HAVE_SYS_STAT_H
#include    <sys/stat.h>
#endif

#ifdef HAVE_SYS_TIME_H
#include	<sys/time.h>  /* for Linux 2.2.x, for struct timeval */
#endif

#if defined(SUPPORT_GGILIB)
#include 	"cceggi.h"
#endif

#if defined(SUPPORT_SDLLIB)
#include 	"ccesdl.h"
#endif

#if defined(SUPPORT_ENCODING_FILTER)
#include	"encfilter.h"
#endif

#define NR_TERMS    10
int numTerms = 0;
int termMask[NR_TERMS];

ConInfo *curCon = NULL;

#ifdef SUPPORT_POSIX_TERMIOS
static struct termios	oldTio;
#endif

int VtNum;  /* current virtual terminal */ 
int StartFromConsole = 1;  /* 0 for X Windows */

int GGIAvail, SDLAvail;   /* GGI or SDL library available? */
int UseGGIFirst, UseSDLFirst;  /* Use GGI/SDL on console ? */

#ifdef __BEOS__
#undef HAVE_SELECT  /* hack for BeOS R5, its select() can work on socket only!*/

#endif

struct initInfo {
    bool display;                        /* display initialized */
    bool termios;                         /* termios saved */
	bool got_oldtio;
};

static struct initInfo init;

static void TermClose(ConInfo *con);
static void SetSignalHandlers(void);

static void CleanUp(void)
{

#ifdef SUPPORT_POSIX_TERMIOS
    if (init.termios)
		tcsetattr(0, TCSANOW, &oldTio);
#endif

#ifdef SIGCHLD
    signal(SIGCHLD, SIG_DFL);  
#endif
#ifdef SIGHUP
    signal(SIGHUP, SIG_DFL);
#endif
#ifdef SIGTERM
    signal(SIGTERM, SIG_DFL);
#endif
#ifdef SIGSEGV
    signal(SIGSEGV, SIG_DFL);
#endif
#ifdef SIGUSR1
    signal(SIGUSR1, SIG_DFL);
#endif
#ifdef SIGUSR2
    signal(SIGUSR2, SIG_DFL);
#endif

    ChildCleanup();
    InputCleanup();

    if (init.display)  
        ConsoleCleanup();
	
	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	KeymapCleanup();
 
		MouseCleanup();
	}

    FontDetach(TRUE);  /* remove loaded font */

#ifdef SUPPORT_ENCODING_FILTER
	    if (UseEncodingFilter) CleanupEncodingFilter();
#endif
		
	if (/*(dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL) &&*/ StartFromConsole)
	{ 
		fprintf(stdout, "\x1B[2J\x1B[1;1;f"); 
		fflush(stdout);
	}

    fprintf(stderr,"\r\nCCE> Finished without core dump :) \r\n");
	if (dispInfo.gxdim >= 1280 && fontheight < 24)
		fprintf(stderr, "CCE> You can try 24x24 font by changing config file " CONFIG_NAME ".\r\n");
	fprintf(stderr, "\r\n");
	
}

static void ExitTerm(int signum)
{

#if defined(__SunOS__) || defined(__SCO__)
	fatal("%s\n", _sys_siglist[signum]);
#elif defined(__Minix__) || defined(__WINDOWS__)
	fatal("SIG\n");  /* no siglist in Minix? */
#else 
    fatal("%s\n",sys_siglist[signum]);  /* Please change it to fatal("SIG\n") if sys_siglist is not defined! */
#endif

}

static fd_set origReadFds;
static int maxFds = 0;

void MouseSetRfd(int mfd)
{
	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	if (mfd > 0) FD_SET(mfd, &origReadFds);
    
    	if (mfd >= maxFds)
        	maxFds = mfd + 1;
	}
}

void MouseResetRfd(int mfd)
{
	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	if (mfd > 0) FD_CLR(mfd, &origReadFds);
	}
}

static void	ConsoleHandler(void)
{
    static u_char	buff[BUFSIZ];
	int remain_bytes;
    fd_set readFds;
    int	j,k, nRead;
#ifdef HAVE_SELECT
    struct timeval tv;
#endif
    ConInfo *con;
#ifdef SUPPORT_GGILIB
	gii_event_mask ggiEvMask;
#endif

	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	if (mInfo.has_mouse && mouseFd > 0)
			MouseSetRfd(mouseFd);
    	else
			MouseResetRfd(mouseFd);

#if !defined(HAVE_SELECT) && defined(O_NONBLOCK)
		fcntl(0, F_SETFL, O_NONBLOCK);  /* make the stdin non-blocking */
#endif
	}

    while (curCon && numTerms > 0)   /* forever looping */
    {
		int	tryagain = 0, v = 1;
	
		do   /* wait until something happens */
 		{

	    	PollCursor(FALSE);

	    	readFds = origReadFds;

#ifdef HAVE_SELECT
	    	tv.tv_sec = 0;
	    	tv.tv_usec = 100000; /* 0.1 sec */
#endif
			switch(dispInfo.type)
			{
#ifdef SUPPORT_GGILIB				
			case TYPE_GGI:

				ggiEvMask = emKey | emPointer;
				v = GGIEVENTSELECT(ggiVis, &ggiEvMask, maxFds, &readFds, NULL, NULL, &tv);
				tryagain = (ggiEvMask == 0 && (v == 0 || (v < 0 && errno == EINTR)));

				break;
#endif /* SUPPORT_GGILIB */

#ifdef SUPPORT_SDLLIB
			case TYPE_SDL:
#ifdef HAVE_SELECT
				tv.tv_usec = 50000;   /* 0.05 second */
				v = select(maxFds, &readFds, NULL, NULL, &tv);
			 	tryagain = (v == 0 || (v < 0 && errno == EINTR));
#endif				
				HandleSDLEvents(curCon->masterPty);
				break;
#endif /* SUPPORT_SDLLIB */			

			default:
#ifdef HAVE_SELECT
	    		v = select(maxFds, &readFds, NULL, NULL, &tv);
			 	tryagain = (v == 0 || (v < 0 && (errno == EINTR || mouseFd < 0)));

#endif
				break;
			} /* switch */

		} 
		while(tryagain); 		
			
		if (v < 0) PerrorExit("select"); /* necessary ? */
   
     	switch(dispInfo.type)
		{
#ifdef SUPPORT_GGILIB
		case TYPE_GGI:
			if (ggiEvMask)	
			{
	    		HandleGIIEvents(curCon->masterPty);		
    			PollCursor(TRUE);
			}
			break;
#endif

#ifdef SUPPORT_SDLLIB
		case TYPE_SDL:  /* Already handled before */
			break;
#endif

		default:

			if (curCon && FD_ISSET(0, &readFds)) 
    		{
	    		nRead = read(0, buff, sizeof(buff));
       
				for(j=0; j <nRead; j++)
 					ProcessNormalModeKey(curCon->masterPty,buff[j]);

	    		PollCursor(TRUE);
			}

			if (mInfo.has_mouse && curCon && mouseFd > 0) 
			{
	    		if (FD_ISSET(mouseFd, &readFds)) 
        		{
					MouseHandleEvent(mouseFd);

					PollCursor(TRUE);
	    		}
			}
			break;
		} /* switch */

		nRead = 0; /* remaining bytes in buff */
		for(k = numTerms, con = curCon; k > 0; k--)
    	{
			if (FD_ISSET(con->masterPty, &readFds)) 
        	{
	    		nRead += read(con->masterPty, buff+nRead, sizeof(buff)-nRead);
				if (nRead > 0)
            	{
		    		if (text_mode) 
					{
		        		write(1, buff, nRead);  /* output to stdout, before CCE start up? */
						nRead = 0; /* force it to be 0, ignore the error condition */
					}
             		else 
                	{
#ifdef SUPPORT_ENCODING_FILTER
						if (UseEncodingFilter) /* all the input go through filter */
						{
							nRead = DoEncodingFilter(CONVERT_FROM_FILTER, buff, nRead);
							if (EncodingFilterLen > 0) VtWrite(con, EncodingFilterBuff, EncodingFilterLen);
						}
						else
#endif
						{
							VtWrite(con, buff, nRead);
							nRead = 0; /* force it to be 0 */
						}

		        		if (con == curCon) TextRefresh(curCon);
		    		}
	        	}
    		}
           
        	con = con->nextCon;
        	if (con->prevCon->terminate)
        	{
        		FD_CLR(con->prevCon->masterPty, &origReadFds);
            	TermClose(con->prevCon);
        	}
		}

    } /* while */
}

int OpenConsole(int flags)
{
	int cfd;

#if defined(linux)
    cfd = open("/dev/console", flags);  /* crw--w--w  root root /dev/console */
    if (cfd < 0) cfd = open("/dev/tty0", flags); 

#elif defined(__FreeBSD__)
	cfd = open("/dev/ttyv0", flags);
    if (cfd < 0) cfd = open("/dev/console", flags);

#elif defined(__NetBSD__)
	cfd = open("/dev/ttyE0", flags);
	if (cfd < 0) cfd = open("/dev/ttyE0", flags); 

#elif defined(__OpenBSD__)
	cfd = open("/dev/console", flags);
	if (cfd < 0) cfd = open("/dev/ttyC0", flags);
	
#elif defined(__Lynx__)  
	cfd = open("/dev/con", flags);
    if (cfd < 0) cfd = open("/dev/atc0", flags); 

#else /* using /dev/console ok?  */
	cfd = open("/dev/console", flags);
#endif

	return cfd;
}

static void GetConsoleVtNum(void)
{
	int cfd, mode;
#if defined(linux) || defined(__Lynx__)
    struct vt_stat vts;
#endif
	
	VtNum = 0; /* default is 0  (like Solaris 9, only one console terminal) */

	cfd = OpenConsole(O_RDONLY);
	if (cfd < 0) 
		message("Failed to open console device.\n"); 

#if defined(linux) || defined(__Lynx__)
    ioctl(cfd, VT_GETSTATE, &vts);
    VtNum = vts.v_active; 
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
   ioctl(cfd, VT_GETACTIVE, &VtNum);
#endif

   close(cfd);

}

#if defined(linux)
#include <linux/major.h>  /* for TTY_MAJOR */
#define CCE_TTY_MAJOR TTY_MAJOR
#elif defined(__FreeBSD__) || defined(__OpenBSD__)  
#define CCE_TTY_MAJOR  12   /* ttyv? ttyC? */

#elif defined(__NetBSD__)
#define CCE_TTY_MAJOR  47   /* ttyE? */

#elif defined(__Lynx__)
#define CCE_TTY_MAJOR  4    /* atc? */

#elif defined(__SCOOpenServer__)
#define CCE_TTY_MAJOR  0   /* SCO OpenServer 5.0.7 /dev/ttyNN (0,0)-(0,N) */
#elif defined(__QNX__)
#define CCE_TTY_MAJOR   5   /* dev/con1 ~ con4 MAJOR is 4 or 5?! FIXME! */
#define CCE_MAJOR(dev) ((dev) >> 10)   
#define CCE_MINOR(dev) ((dev) & 0x3FF)
#elif defined(__SunOS__)
#define CCE_TTY_MAJOR	0 	/* dev/console? (0,0) ? FIXME */
#else
#define CCE_TTY_MAJOR  4    /* most Unix use 4 for console tty major?  */
#endif

#if !defined(CCE_MAJOR)
#define CCE_MAJOR(dev)   ((dev) >> 8)  
#endif
#if !defined(CCE_MINOR)
#define CCE_MINOR(dev)	 ((dev) & 0xFF)
#endif

int IsConsoleTTY(int fd)
{
	struct stat ttystat;
	
	if (!isatty(fd)) return 0;

#ifdef HAVE_TTYNAME
	if (!ttyname(fd) || stat(ttyname(fd), &ttystat) == -1) 
#endif
		return 0;  /* if no ttyname(), probabaly always GUI OS, return 0 */

	if (!strcmp(ttyname(fd), "/dev/console")) return 1;

#if defined(__Mach__) || defined(__Hurd__) || defined(__Darwin__)
	if (strcmp(ttyname(fd), "/dev/console")) return 0;
#elif defined(linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__Lynx__) || defined(__SunOS__)  || defined(__QNX__) || defined(__Minix) || defined(__SCOOpenServer__)
	if (CCE_MAJOR(ttystat.st_rdev) != CCE_TTY_MAJOR) 
	{
#if defined(__NetBSD__)
		if (!strncmp(ttyname(fd), "/dev/ttyE", 9)) return 1; /*FIXME,hack for changing TTY_MAJOR! */
#elif defined(__OpenBSD__)
		if (!strncmp(ttyname(fd), "/dev/ttyC", 9)) return 1;
#endif
		return 0;
	}
#elif defined(__WINDOWS__) || defined(__BEOS__)
	return 0; /* always full screen GUI */
#elif defined(__SCOUnixWare__)
	if (strncmp(ttyname(fd), "/dev/vt",7))
		return 0;
#else

    return 1;
#endif

#if defined(linux)
	if (CCE_MINOR(ttystat.st_rdev) > 63) return 0;
#endif
	
	return 1;  /* default is 1 ? */
}

void	TermInit()
{
    init.display = init.termios = init.got_oldtio = FALSE;

    text_mode = TRUE;

#ifdef SUPPORT_POSIX_TERMIOS
	if (isatty(0))  /* in X Windows, it's possible that 0 is not tty! */
	{

		if (tcgetattr(0, &oldTio) == -1)
		{
		}
		else init.got_oldtio = TRUE;
		
	}		
#endif  /* SUPPORT_POSIX_TERMIOS */

	StartFromConsole = IsConsoleTTY(fileno(stdin)); 

#ifdef __Darwin__
	UseGGIFirst = UseSDLFirst = 1;  /* hack for Mac OS X */
#endif
#ifdef SUPPORT_GGILIB
	if (UseGGIFirst) GGIAvail = IsGGIAvailable();
#endif
#ifdef SUPPORT_SDLLIB
	if (!GGIAvail && UseSDLFirst)  SDLAvail = IsSDLAvailable();
#endif

#if !defined(__MSDOS__) 

#if defined(SUPPORT_VGA) || defined(SUPPORT_FRAMEBUF)
	if (!GGIAvail && !SDLAvail && !StartFromConsole)  /* VGA and FrameBuf works in console only */
#endif
	{
#if defined(SUPPORT_GGILIB)  /* try GGI first */
		if (!UseGGIFirst) GGIAvail = IsGGIAvailable();/* GGI supports X Windows  */
#endif

#if defined(SUPPORT_SDLLIB)  /* try SDL second */
		if (!GGIAvail && !UseSDLFirst) SDLAvail = IsSDLAvailable();
#endif
	
		if (!GGIAvail && !SDLAvail)
		{
#if !defined(DEBUG) 
			fatal("No GGI/SDL found, you can only run CCE in text console.\n");
#endif /* DEBUG */
		}
	}

#endif /* __MSDOS__ */

    CapInit();
    ChildInit();

	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	MouseInit();  
    	KeymapInit();
	}

	VtInit();/*jmt:2k0826:moved from (b4 ConsoleInit) to (b4 InputInit) // Big5 jmce */
    FontInit();

    ConsoleInit();

    InputInit();
    
	GetConsoleVtNum();  /* try to get the VtNum (for use in hzinput.c) */
}

void ChangeTerm(int direction)
{
    if (direction)
        curCon = curCon->nextCon;
    else
        curCon = curCon->prevCon;
    TextRepaintAll(curCon);
}

static void TermClose(ConInfo *con)
{
    sigset_t mask;

    if (con == NULL) return;

#ifdef SIGCHLD  /* MinGW32 has no SIGCHLD */
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, NULL);
#endif

    if (con != con->nextCon) /* all console is closed? */
    {
        con->prevCon->nextCon = con->nextCon;
        con->nextCon->prevCon = con->prevCon;
    }
    else curCon = NULL;

    numTerms--;
    termMask[con->window] = 0;

#ifdef SIGCHLD 
    sigprocmask(SIG_UNBLOCK, &mask, NULL);
#endif

    if (curCon == con)
       ChangeTerm(1);
    close(con->masterPty);
    VtCleanup(con);
}

void SetTermWinSize(int fd, int row, int col)
{
#ifdef HAVE_TIOCWINSZ
	struct winsize win;
	
	if (row <= 0) row = dispInfo.tymax;	
   	if (col <= 0) col = dispInfo.txmax;

	win.ws_row = row;
	win.ws_col = col;
   	ioctl(fd, TIOCSWINSZ, &win);
#endif
}

#if defined(__BEOS__)
#define CCE_PTY_DEVICE  "/dev/pt/"
#elif defined(__SunOS__) || defined(__WINDOWS__)
#define CCE_PTY_DEVICE "/dev/ptmx"  /* System V style, cygwin also does this */
#else
#define CCE_PTY_DEVICE  "/dev/pty"  /* BSD style pty & tty */
#endif

#define PTY_ERRMSG	"Failed to open new pseudo TTY.\r\n"

#if defined(__MSDOS__) || defined(__MINGW32__)  

int TermFork(ConInfo *con)
{

    return 1; 
}

#else /* __MSDOS__ */

int TermFork(ConInfo *con)
{
    char ls, ln;
	int  slavePty;
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 
    int devtty;
#elif defined(__SunOS__) || defined(__WINDOWS__)
	u_char *ttyName;
#endif

#if defined(__SunOS__) || defined(__WINDOWS__)
	
	strcpy(con->ptyName, CCE_PTY_DEVICE);
  	if ((con->masterPty = open(con->ptyName, O_RDWR | O_NOCTTY)) == -1 ||
	    grantpt(con->masterPty) || unlockpt(con->masterPty) ||
    	(ttyName = ptsname(con->masterPty)) == NULL)
    {
    	if (con->masterPty >= 0) close(con->masterPty);
        PerrorExit(PTY_ERRMSG);
    }
  	strncpy(con->ptyName, ttyName, sizeof(con->ptyName));

#else
    for (ls = 'p'; ls <= 's'; ls ++)
    {
        for (ln = 0; ln <= 0xF; ln ++)
        {

            snprintf(con->ptyName, sizeof(con->ptyName), CCE_PTY_DEVICE "%1c%1x", ls, ln);
#ifndef HAVE_SELECT
			if ((con->masterPty = open(con->ptyName, O_RDWR|O_NONBLOCK)) >= 0) break;
#else
            if ((con->masterPty = open(con->ptyName, O_RDWR)) >= 0) break;
#endif
        }
        if (con->masterPty >= 0) break;
    }

    if (con->masterPty < 0)
        PerrorExit(PTY_ERRMSG);

    con->ptyName[5] = 't';   /* slave tty */

#endif

    if ((con->childPid = fork()) < 0)
        PerrorExit("fork");
    else if (con->childPid == 0)  /* in child process */
    {
        int     efd;
        FILE    *errfp;

        efd = dup(2);
        errfp = fdopen(efd, "w");

        setsid();

#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 
        if ((devtty = open("/dev/tty",O_RDWR|O_NONBLOCK)) >= 0)
        {
            ioctl(devtty, TIOCNOTTY, (char *)0);

            close(devtty);
        }
#endif

        if ((slavePty = open(con->ptyName, O_RDWR)) < 0)
            PerrorExit(con->ptyName);

#if defined(__SunOS__)
		if (ioctl(slavePty, I_PUSH, "ptem") < 0)
			PerrorExit("ioctl(I_PUSH, ptem)");
		if (ioctl(slavePty, I_PUSH, "ldterm") < 0)
			PerrorExit("ioctl(I_PUSH, ldterm)");
		if (ioctl(slavePty, I_PUSH, "ttcompat") < 0)
			PerrorExit("ioctl(I_PUSH, ttcompat)");
#endif

        close(con->masterPty);

		if (init.got_oldtio)
		{
			tcsetattr(slavePty, TCSANOW, &oldTio);
		}

#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
        ioctl(slavePty, TIOCSCTTY, (char *)0);

#endif

		SetTermWinSize(slavePty, 0,0);  /* use defauult size */
		
        dup2(slavePty, 0);
        dup2(slavePty, 1);
        dup2(slavePty, 2);

        close(slavePty);

        ChildStart(errfp);  /* execute the shell */
    }
  
    return 1; /* parent process */
}

#endif  /* __MSDOS__ || __MINGW32__ */

void TermOpen(void)
{
    ConInfo *con;
    sigset_t mask;
    int i;

    if (numTerms < NR_TERMS)
    {
        con = malloc(sizeof(ConInfo));
		if (con == NULL)
	 	{
             error("Open New TTY failed\n");
             return;
        }     
         
		VtStart(con); /* initialize all the buffer and variables, move it here for safety! */

        if (TermFork(con))  /* parent process */
        {
#ifdef SIGCHLD  /* MinGW32 has no SIGCHLD */
        	sigemptyset(&mask);
            sigaddset(&mask, SIGCHLD);
            sigprocmask(SIG_BLOCK, &mask, NULL);
#endif
			if (curCon)
            {
                con->prevCon = curCon;
                con->nextCon = curCon->nextCon;
                curCon->nextCon->prevCon = con;
                curCon->nextCon = con;
           	}
            else curCon = con->nextCon = con->prevCon = con;
            numTerms++;
             
            for(i = 0; i < NR_TERMS; i++)
            	if (termMask[i] == 0)
               	{
                  	con->window = i;
                  	termMask[i] = 1;
                  	break;
               	}  /* find an unused window */
#ifdef SIGCHLD            
         	sigprocmask(SIG_UNBLOCK,&mask, NULL);
#endif
            curCon = con;  /* activate current */

            FD_SET(con->masterPty,&origReadFds);
            if (con->masterPty >= maxFds)
				maxFds = con->masterPty+1;
		}
    }
}

void SetConsoleRawMode(void)
{
#ifdef SUPPORT_POSIX_TERMIOS 
    struct termios       newTio;

	if (isatty(0) && init.got_oldtio)  /* In XWindows, probably it's not tty! */
	{
		init.termios = TRUE;

    	newTio = oldTio;

    	newTio.c_lflag &= ~(ECHO|ISIG|ICANON|XCASE);
    	newTio.c_iflag &= 0;
    	newTio.c_cflag |= CS8;
    	newTio.c_oflag &= ~(OPOST);
    	newTio.c_cc[VMIN] = 1;
    	newTio.c_cc[VTIME] = 0;
	
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) 
    	newTio.c_cc[VDISCARD] = _POSIX_VDISABLE;
    	newTio.c_cc[VLNEXT] = _POSIX_VDISABLE;
    	newTio.c_cc[VSTART] = _POSIX_VDISABLE;
    	newTio.c_cc[VSTOP] = _POSIX_VDISABLE;
    	newTio.c_cc[VINTR] = _POSIX_VDISABLE;
    	newTio.c_cc[VSUSP] = _POSIX_VDISABLE;
    	newTio.c_cc[VDSUSP] = _POSIX_VDISABLE;
    	newTio.c_cc[VQUIT] = _POSIX_VDISABLE;

#elif defined(linux) 
    	newTio.c_line = 0;
#endif

		tcsetattr(0, TCSANOW, &newTio);

	}

#endif /*  SUPPORT_POSIX_TERMIOS */
}

void	TermStart(void)
{
	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)
	{
    	if (mInfo.has_mouse) 
    	{
			mouseFd = MouseAttach();
    	}
	}

    atexit(CleanUp);  /* program exit cleanup */

    SetConsoleRawMode();
    SetSignalHandlers();
   
    FD_ZERO(&origReadFds);

	if (dispInfo.type != TYPE_GGI && dispInfo.type != TYPE_SDL)

    	FD_SET(0, &origReadFds);  /* from console */

    FontAttach();

    TermOpen();

    ConsoleStart();  /* switch to graph mode  */
    init.display = TRUE;

#ifdef SUPPORT_ENCODING_FILTER
	if (UseEncodingFilter) SetupEncodingFilter(lInfo.sysCoding);
#endif

    ConsoleHandler();   /* main loop event */
}

void SigChildHandler(int signo)
{
#ifdef HAVE_WAITPID
	int status;
	ConInfo *con;

	con = curCon;
	if (curCon)
	{
  		do
  		{
     		if (waitpid(con->childPid,&status, WNOHANG))
       			con->terminate = 1;
     		con = con->nextCon;
  		} while (con != curCon );
	}
	
  	signal(SIGCHLD, SigChildHandler);
#endif
}

static void SetSignalHandlers(void)
{

#ifdef SIGCHLD
    signal(SIGCHLD, SigChildHandler); 

#endif
#ifdef SIGHUP
    signal(SIGHUP, ExitTerm);
#endif
#ifdef SIGTERM
	signal(SIGTERM, ExitTerm);
#endif

#if defined(SIGSEGV) && !defined(DEBUG) 
    signal(SIGSEGV, ExitTerm);
#endif

#ifdef SIGQUIT
    signal(SIGQUIT, ExitTerm);
#endif
#ifdef SIGILL
    signal(SIGILL, ExitTerm);
#endif
#ifdef SIGABRT
    signal(SIGABRT, ExitTerm);
#endif
}

int ChangeSystemEncoding(int encoding)
{

   	if (encoding == -1)
		encoding = lInfo.sysCoding + 1;
	
	while(encoding  != lInfo.sysCoding 
			&& lInfo.sysCoding >= CODE_EUC && lInfo.sysCoding <= CODE_KSC)
   	{
   		if (encoding < CODE_EUC || encoding > CODE_KSC) encoding = CODE_EUC; /* 1 */

   		if (MmapFont(encoding, NULL) == 0)
		{
			lInfo.sysCoding = encoding;

			InputSwitchEncoding(encoding); /* handle input-related stuff */

#ifdef SUPPORT_ENCODING_FILTER
			if (UseEncodingFilter) SetupEncodingFilter(lInfo.sysCoding);
#endif
				 
			TextRepaintAll(curCon);  /* repaint display and input area */

			return 0;
		}

		encoding++;
	}

	return -1;
}

