/* vi: set sw=4 ts=4: */
/*
 * KON2 - Kanji ON Console -
 * Copyright (C) 1992, 1993 MAEDA Atusi (mad@math.keio.ac.jp)
 * 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	<string.h>
#include	<unistd.h>
#include	<fcntl.h>
#include	<signal.h>

#include	"defs.h"
#include	"errors.h"
#include	"getcap.h"
#include	"font.h"
#include	"mouse.h"
#include	"vtcon.h"
#include	"vc.h"
#include	"term.h"
#include	"key.h"
#include	"hzinput.h"
#include	"vga.h"
#include	"fb.h"
#include 	"encfilter.h"

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

DispInfo	dispInfo;
CursorInfo	cursorInfo;
VideoInfo	*pVideoInfo = NULL;
CursorInfo	mouseCursor;

static char DisplayName[10];  /* VGA or S3 or... buffer overflow? */

#define CCE_HANDLE_VTSWITCH   (StartFromConsole && dispInfo.type != TYPE_SDL)

int text_mode = FALSE;
int curActive = FALSE;

static volatile bool	busy;		 /* TRUE iff updating screen */
static volatile bool	release;	 /* delayed VC switch flag */

static void	ShowCursor(CursorInfo *, bool);
static void	LeaveVC(int);
static void	EnterVC(int);
static void TextClearBand(ConInfo *con, int top, int btm);

#define	CCE_MOUSE_TMPFILE	 "/.cCeMsEtMp"  /* should we use file or memory to do it? */

static char tmpFileName[CCE_PATHNAME_MAX];

static inline void    blatch(void *head, int n)
{
	while (n-- > 0)
	{
		(*(unsigned char *)head) &= (unsigned char)0x7F;
		((unsigned char *)head)++;
	}
}

static inline   void    llatch(void *head, int n)
{
	n >>= 2;  /* convert into 32 bits */
    while (n-- > 0)
    {
        (*(unsigned int *)head) &= (unsigned long)0x7F7F7F7F;
        ((unsigned int *)head)++;
    }
}

static inline bool	IsKanji(ConInfo *con,u_int x, u_int y)
{
    return(*(con->flagBuff + TextAddress(con,x, y)) & CODEIS_1);
}

static inline bool	IsKanji2(ConInfo *con,u_int x, u_int y)
{
    return(*(con->flagBuff + TextAddress(con,x, y)) & CODEIS_2);
}

void	TextDeleteChar(ConInfo *con, int n)
{
    u_int	addr, dx;
    
    addr = TextAddress(con, con->x, con->y);
    dx = dispInfo.txmax - con->x - n;
    
#ifdef DEBUG
#endif

    bmove(con->textBuff + addr, con->textBuff + addr + n, dx);
    bmove(con->attrBuff + addr, con->attrBuff + addr + n, dx);
    blatch(con->flagBuff + addr, dx);
    
    addr = TextAddress(con, dispInfo.txmax - n, con->y);

	memset(con->textBuff + addr, 0x20, n);
    memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, n);
    bzero2(con->flagBuff + addr, n);
}

void	TextInsertChar(ConInfo *con,int n)
{
    u_int	addr, dx;
    
    addr = TextAddress(con, dispInfo.txmax, con->y);  /* txmax for example, 128 */
    dx = dispInfo.txmax - con->x - n;
    
#ifdef DEBUG
#endif

    brmove(con->textBuff + addr, con->textBuff + addr - n, dx);
    brmove(con->attrBuff + addr, con->attrBuff + addr - n, dx);
    
    addr = TextAddress(con,con->x, con->y);
    blatch(con->flagBuff + addr + n, dx);
	memset(con->textBuff + addr, 0x20, n);
    memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, n);
    bzero2(con->flagBuff + addr, n);
}

void    TextMoveUp(ConInfo *con,int top, int btm, int line)
{
    int       n, src, dst;

    if (btm - top - line + 1 <= 0)
    {
        TextClearBand(con,top, btm);
        return;
    }
    for (n = top; n <= btm - line; n ++)
    {
        dst = TextAddress(con,0, n);
        src = TextAddress(con,0, n + line);
        lmove(con->textBuff + dst, con->textBuff + src, dispInfo.txmax);
        lmove(con->attrBuff + dst, con->attrBuff + src, dispInfo.txmax);
        lmove(con->flagBuff + dst, con->flagBuff + src, dispInfo.txmax);
        llatch(con->flagBuff + dst, dispInfo.txmax);
    }
    TextClearBand(con,btm - line + 1, btm);
}

void    TextMoveDown(ConInfo *con,int top, int btm, int line)
{
    int       n, src, dst;

    if (btm - top - line + 1 <= 0)
    {
        TextClearBand(con,top, btm);
        return;
    }
    for (n = btm; n >= top + line; n --)
    {
        dst = TextAddress(con,0, n);
        src = TextAddress(con,0, n - line);
        lmove(con->textBuff + dst, con->textBuff + src, dispInfo.txmax);
        lmove(con->attrBuff + dst, con->attrBuff + src, dispInfo.txmax);
        lmove(con->flagBuff + dst, con->flagBuff + src, dispInfo.txmax);
        llatch(con->flagBuff + dst, dispInfo.txmax);
    }
    TextClearBand(con,top, top + line - 1);
}

void    ScrollUp(ConInfo *con,int line)
{

    {
        TextMoveUp(con, con->ymin, con->ymax, line);
    }
}

void    ScrollDown(ConInfo *con,int line)
{

    {
        TextMoveDown(con,con->ymin, con->ymax, line);
    }
}

void TextClearAll(ConInfo *con)
{
    memset(con->textBuff, 0x20, con->textSize);
    memset(con->attrBuff, ((con->bcol << 4) | con->fcol), con->textSize);
    lzero(con->flagBuff, con->textSize);
    mInfo.sw = 0;
    con->textClear = TRUE;
}

void    TextClearEol(ConInfo *con,u_char mode)
{
    u_int       addr;
    u_char      len, x=0;

    switch(mode) 
    {
    case 1: /* erase from start of line to cursor(including cursor) */
        len = con->x + 1;  /* Rui He Sep 28 fixed, added 1 */
        break;
    case 2: /* erase whole line */
        len = dispInfo.txmax;  /* 80/128 etc */
        break;
    default:  /* 0  erase from cursor to end of line  */
        x = con->x;
        len = dispInfo.txmax - con->x;
        break;
    }
    addr = TextAddress(con, x, con->y);
    memset(con->textBuff + addr, 0x20, len);
    memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, len);
    bzero2(con->flagBuff + addr, len);/* needless to latch */
}

void    TextClearEos(ConInfo *con,u_char mode)
{
    int       addr, len, y;

    if (mode == 2)
    {
        TextClearAll(con);
        return;
    }
    switch(mode)
    {
    case 1:
        for (y = 0; y < con->y; y ++)
        {
            addr = TextAddress(con,0, y);

            memset(con->textBuff + addr, 0x20, dispInfo.txmax);
            memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, dispInfo.txmax);
            lzero(con->flagBuff + addr, dispInfo.txmax);

        }
        addr = TextAddress(con,0, con->y);
        memset(con->textBuff + addr, 0x20, con->x);
        memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, con->x);
        bzero2(con->flagBuff + addr, con->x);  /* needless to latch */
        break;
    default:
        for (y = con->y + 1; y < dispInfo.tymax; y ++)
        {
            addr = TextAddress(con,0, y);
            memset(con->textBuff + addr, 0x20, dispInfo.txmax);
            memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, dispInfo.txmax);
            lzero(con->flagBuff + addr, dispInfo.txmax);

        }
        addr = TextAddress(con,con->x, con->y);
        len = dispInfo.txmax - con->x;
        memset(con->textBuff + addr, 0x20, len);
        memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, len);
        bzero2(con->flagBuff + addr, len);  /* needless to latch */
        break;
    }
}

static void     TextClearBand(ConInfo *con, int top, int btm)
{
    int       y, addr;

    for (y = top; y <= btm; y ++)
    {
        addr = TextAddress(con,0, y);
        memset(con->textBuff + addr, 0x20, dispInfo.txmax);
        memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, dispInfo.txmax);
        lzero(con->flagBuff + addr, dispInfo.txmax);/* needless to latch */
    }
}

void TextClearChars(ConInfo * con, int len)
{
     int addr;
     
     if ( con->x + len > dispInfo.txmax )
        len = dispInfo.txmax - con->x;

     addr = TextAddress(con, con->x, con->y);
     memset(con->textBuff + addr, 0x20, len);
     memset(con->attrBuff + addr, (con->bcol << 4) | con->fcol, len);
     bzero2(con->flagBuff + addr, len);  /* needless to latch */
}

static inline void KanjiAdjust(ConInfo *con,int *x, int *y)
{
    if (IsKanji2(con,*x, *y))
    {
        --*x;
    }
}

static u_char paste_buff[256];

void    TextCopy(ConInfo *con,int fx, int fy, int tx, int ty)
{
    int fd;
    int       from, to, y, swp, xx, x;
    u_char      ch, ch2;
	int buff_len = 0;

    unlink(tmpFileName); 

    if ((fd = open(tmpFileName, O_WRONLY|O_CREAT, 0600)) < 0)
	{
		return;
	}

    KanjiAdjust(con,&fx, &fy);
    KanjiAdjust(con,&tx, &ty);
    if (fy > ty)
    {
        swp = fy; fy = ty; ty = swp;
        swp = fx; fx = tx; tx = swp;
    }
    else if (fy == ty && fx > tx)
    {
        swp = fx; fx = tx; tx = swp;
    }

	xx = dispInfo.txmax-1;
    for (y = fy; y <= ty; y ++)
    {
        if (y == ty) xx = tx;
        from = TextAddress(con,fx, y);
        if (con->flagBuff[from] & CODEIS_2)

            from--;
        to = TextAddress(con,xx, y);
        for (x = to; x >= from; x --)
           if (con->textBuff[x] > ' ') break;
        to = x;

        for (x = from; x <= to; x++)
        {
            ch = con->textBuff[x];
            if (!ch) ch = ' ';
            if (con->flagBuff[x] & CODEIS_1) 
            {
                x++;
                ch2 = con->textBuff[x];
                switch(lInfo.sysCoding)
                {
                case CODE_EUC:
                    ch2 |= 0x80;
                    ch |= 0x80;
                    break;
                case CODE_SJIS:
					jistosjis(ch, ch2);
                    break;
                }

				paste_buff[buff_len++] = ch;
				paste_buff[buff_len++] = ch2;
            } 
			else paste_buff[buff_len++] = ch;

			if (buff_len >= sizeof(paste_buff)-4) /* leave some room for '\n' below and next two bytes  */
			{
#ifdef SUPPORT_ENCODING_FILTER
				if (UseEncodingFilter)
				{
					buff_len = DoEncodingFilter(CONVERT_TO_FILTER, paste_buff, buff_len);
					if (EncodingFilterLen > 0) write(fd, EncodingFilterBuff, EncodingFilterLen);
				}
				else
#endif
				{
					write(fd, paste_buff, buff_len);
					buff_len = 0;
				}
			}
			
        }  /* for x */

        if (y < ty)
        {

			paste_buff[buff_len++] = '\n';
        }
        fx = 0;

    }  /* for xx */
	
	if (buff_len > 0)
	{
#ifdef SUPPORT_ENCODING_FILTER
		if (UseEncodingFilter)  /* converted from native to target filter encoding */
		{
			buff_len = DoEncodingFilter(CONVERT_TO_FILTER, paste_buff, buff_len);
			if (EncodingFilterLen > 0) write(fd, EncodingFilterBuff, EncodingFilterLen);
		}
		else
#endif
			write(fd, paste_buff, buff_len); /* write the remaining */
	}
    close(fd);
}

void    TextPaste(ConInfo *con)
{
    u_char      ch;
    int fd, i;
	size_t nRead;

    if ((fd = open(tmpFileName, O_RDONLY)) < 0) return;
    while( (nRead = read(fd, paste_buff, sizeof(paste_buff))) > 0)  
	{
		for(i = 0; i < nRead; i++)
		{
        	if (paste_buff[i] == 0xa)  /* necessary? */
        		paste_buff[i] = 0xd;  /* Line bug.. */
		}
		
        write(con->masterPty, paste_buff, nRead);
	}
    close(fd);
}

void    TextReverse(ConInfo *con,int fx, int fy, int tx, int ty)
{
    int       from, to, y, swp, xx, x;
    u_char      fc, bc, fc2, bc2;

    KanjiAdjust(con,&fx, &fy);
    KanjiAdjust(con,&tx, &ty);
    if (fy > ty)
    {
        swp = fy; fy = ty; ty = swp;
        swp = fx; fx = tx; tx = swp;
    }
    else if (fy == ty && fx > tx)
    {
        swp = fx; fx = tx; tx = swp;
    }

    for (xx = dispInfo.txmax-1, y = fy; y <= ty; y ++)
    {
        if (y == ty) xx = tx;
        from = TextAddress(con,fx, y);
        to = TextAddress(con,xx, y);
        if (con->flagBuff[from] & CODEIS_2)

            from--;

        for (x = from; x <= to; x ++)
        {
            if (!con->textBuff[x]) continue;
            fc = con->attrBuff[x];
            bc = fc >> 4;
            bc2 = (bc & 8) | (fc & 7);
            fc2 = (fc & 8) | (bc & 7);
            con->attrBuff[x] = fc2 | (bc2 << 4);
            con->flagBuff[x] &= ~CLEAN_S;
        }
        fx = 0;
    }
}

 void TextWput1(ConInfo *con,u_char ch)
{
    u_int addr;

    addr = TextAddress(con,con->x, con->y);
    con->attrBuff[addr] = con->fcol | (con->bcol << 4);
    con->textBuff[addr] = ch;
    con->flagBuff[addr] = con->db;

#ifdef DEBUG
	fprintf(stderr, "Now in TextWput1, con-X=%d con-y=%d, db=0x%x ch=0x%x\n", con->x, con->y, con->db, ch);
#endif
}

void TextWput2(ConInfo *con,u_char ch)
{
    u_int addr;

    addr = TextAddress(con,con->x, con->y);
    con->textBuff[addr] = ch;
    con->flagBuff[addr] = LATCH_2;
}

void TextWput(ConInfo *con,u_char ch1, u_char ch2)
{
    u_int addr;

#ifdef DEBUG
	fprintf(stderr, "Now in TextWput x=%d y=%d ch1=0x%x ch2=0x%x con->db=0x%x\n", con->x, con->y, ch1, ch2, con->db);
#endif

    addr = TextAddress(con,con->x, con->y);

    con->attrBuff[addr] = con->fcol | (con->bcol << 4);
    con->textBuff[addr] = ch1; /* ch2;  Rui He Jul 9 2003 changed */
    con->flagBuff[addr] = con->db;  /* LATCH_1 | xx */
    con->textBuff[addr+1] = ch2; /* ch1; Rui He Jul 9 2003 changed */
    con->flagBuff[addr+1] = LATCH_2;
}

void TextSput(ConInfo *con,u_char ch)
{
    u_int addr;

    addr = TextAddress(con,con->x, con->y);
    con->flagBuff[addr] = LATCH_S|con->sb;
    con->attrBuff[addr] = con->fcol | (con->bcol << 4);
    con->textBuff[addr] = ch;
}

void	TextRefresh(ConInfo *con)
{
    int	i,j;
    u_char	ch, ch2, fc, bc;
    int color;

    color = (con->attr & ATTR_REVERSE ? con->fcol:con->bcol)&7; 

    busy = TRUE;
    if (!curActive) 
    {
		busy = FALSE;
		return;
    }

    ShowCursor(&cursorInfo, FALSE);
    ShowCursor(&mouseCursor, FALSE);

    if (con->textClear)
		pVideoInfo->clear(CLEAR_DISPLAY, color);

    con->textClear = FALSE;

    for (j = 0; j < con->textSize; j ++) 
    {
        i = (con->textHead +j ) % con->textSize;
		if (con->flagBuff[i] & CLEAN_S) continue;

		fc = con->attrBuff[i] & 0x0F;
		bc = con->attrBuff[i] >> 4;
		ch = con->textBuff[i];
		con->flagBuff[i] |= CLEAN_S;

		if ((con->flagBuff[i] & CODEIS_1)) 
        {

	    	dbFReg = &fDRegs[ con->flagBuff[i] & LANG_CODE ];
	    	i = (i+1) % con->textSize;
	    	con->flagBuff[i] |= CLEAN_S;
	    	ch2 = con->textBuff[i];

			pVideoInfo->wput(lInfo.sysCoding, /*con->flagBuff[i] & LANG_CODE, */
					j % dispInfo.txmax, j / dispInfo.txmax, ch, ch2, fc, bc);
		} 
        else 
        {
	    	sbFReg = &fSRegs[0];  /* Rui He Jul 9 2003 changed to handle half-Chinese problem forced to use English */

			pVideoInfo->sput(j % dispInfo.txmax, j / dispInfo.txmax, ch, fc, bc);
		}
    }
	
    cursorInfo.kanji = IsKanji(con,con->x, con->y);
	cursorInfo.x = con->x;
	cursorInfo.y = con->y;

    ShowCursor(&cursorInfo, TRUE);
    busy = FALSE;
    
	if (CCE_HANDLE_VTSWITCH)
	{
#ifdef SIGUSR1
    	if (release) LeaveVC(SIGUSR1);
#endif
	}

}

void    TextRepaintAll(ConInfo *con)
{
    llatch(con->flagBuff, con->textSize);

    con->currentScroll = con->scrollLine = 0;
    TextRefresh(con);
    RefreshInputArea(TRUE);  /* window-0/1/2.. */
}

#ifdef HAVE_TIOCWINSZ
static struct winsize text_win; /* for the old text window */
#endif

static void SigAlarmHandler(int signum)
{
#ifdef SIGALRM
	signal(SIGALRM, SigAlarmHandler);
    alarm(  60  ); /* set it up again */
#endif

	if (curActive && text_mode == FALSE)
    	RefreshInputArea(FALSE);  
}

static void SetTextMode(void)
{

    ShowCursor(&cursorInfo, FALSE);
    pVideoInfo->text_mode();

	text_mode = TRUE;

#if defined(HAVE_TIOCCONS)
    ioctl(0, TIOCCONS, NULL);  
#endif
}

static void GraphMode(void)
{

#if defined(HAVE_VT_PROCESS_SWITCH)
    struct vt_mode vtm;
#endif	

    text_mode = FALSE;

#ifdef SIGALRM

    signal(SIGALRM, SigAlarmHandler);
    alarm( 60 );  /* 60 seconds, update once per minute */
#endif

	if (CCE_HANDLE_VTSWITCH)
	{

#if defined(__OpenBSD__) || defined(__NetBSD__)
		int mode = WSDISPLAYIO_MODE_MAPPED;  /* 1 */
		ioctl(0, WSDISPLAYIO_SMODE, &mode);
#endif

#if defined(HAVE_KDSETMODE) 
#if defined(SUPPORT_MOUSE) && defined(SUPPORT_GPMMOUSE)
		if (mouseType != MOUSE_GPM) 

#endif
		{
			ioctl(0, KDSETMODE, KD_GRAPHICS);

		}
#endif  /* HAVE_KDSETMODE */

#if defined(HAVE_VT_PROCESS_SWITCH)
	
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)  || defined(__SCO__)
    	ioctl(0, VT_RELDISP, VT_ACKACQ);  /* VT_ACKACQ is 2 */
#endif

    	signal(SIGUSR1, LeaveVC);
    	signal(SIGUSR2, EnterVC);

    	vtm.mode = VT_PROCESS;

    	vtm.waitv = 0;
    	vtm.relsig = SIGUSR1;
    	vtm.acqsig = SIGUSR2;
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
    	vtm.frsig = SIGUSR1; 

#else 
		vtm.frsig = 0; /* add by Rui He  frsig is not used */
#endif

   		ioctl(0, VT_SETMODE, &vtm);
#endif /* HAVE_VT_PROCESS_SWITCH */
	}

 	pVideoInfo->graph_mode();

#if defined(HAVE_TIOCCONS)
   ioctl(curCon->masterPty, TIOCCONS, NULL);   /* redirect system output to pseudo tty */
#endif

   llatch(curCon->flagBuff, curCon->textSize);
 
   TextRefresh(curCon);
   RefreshInputArea(TRUE);
}

void LeaveVC(int signum)
{
#if defined(HAVE_VT_PROCESS_SWITCH)
	if (CCE_HANDLE_VTSWITCH)
	{
    	signal(SIGUSR1, LeaveVC);  /* should use sigaction()? */
    	if (busy) 
    	{
        	release = TRUE;  /* delay  */
			return;
    	}

    	release = FALSE;
    	curActive = FALSE;
    
		SetTextMode();

#if defined(SUPPORT_MOUSE) 
    	if (mInfo.has_mouse) 
    	{
			MouseResetRfd(mouseFd);
			MouseDetach();
    	}
#endif

#if defined(linux) 
    	ioctl(0, VT_RELDISP, 1);  /* no VT_TRUE definition in Linux */
#elif defined(__FreeBSD__)
    	ioctl(0, VT_RELDISP, VT_TRUE);  /* VT_TRUE is 1, user release display */
#else
		ioctl(0, VT_RELDISP, 1);   /* works for Lynx */
#endif

   		RestoreKeymap();
	}

#endif  /* HAVE_VT_PROCESS_SWITCH */
}

static void	EnterVC(int signum)
{
#if defined(HAVE_VT_PROCESS_SWITCH)

	if (CCE_HANDLE_VTSWITCH)
	{
    	signal(SIGUSR2, EnterVC);
    	if (!curActive) 
    	{
			curActive = TRUE;
			GraphMode();

#if defined(SUPPORT_MOUSE) 
			if (mInfo.has_mouse) 
        	{
	    		MouseAttach();
	    		MouseSetRfd(mouseFd);
			}
#endif
		}  /* !curActive */

   		SetupKeymap();
	}

#endif  /* HAVE_VT_PROCESS_SWITCH */
}

static int      saveTime, saverCount;
static bool     saved;

static void     SaveScreen(bool save)
{
    if (saved != save)
    {
        saved = save;
        pVideoInfo->screen_saver(save);
    }
    saverCount = 0;
}

static int      ConfigSaver(const char *confstr)
{
    saveTime = atoi(confstr) * 600; /* convert unit from minitue to 1/10 sec */

    return SUCCESS;
}

static void	ToggleCursor(CursorInfo *c)
{
    c->count = 0;
    if (text_mode) return;
    c->shown = ! c->shown;
    pVideoInfo->cursor(c);  /* draw the cursor using XOR operation */
}

static void	ShowCursor(CursorInfo *c, bool show)
{
    if (!curActive || !c->sw) return;
    if (c->shown != show) ToggleCursor(c);
}

static void PollMouseCursor(void)
{

	if (dispInfo.type == TYPE_SDL ||
		(dispInfo.type == TYPE_GGI && !StartFromConsole)) return; 

    ShowCursor(&mouseCursor, FALSE);
    if (mInfo.sw > 0) 
    {
		--mInfo.sw;
		if (cursorInfo.shown) 
        {
	    	int	x = mInfo.x, y = mInfo.y;
	    
	    	KanjiAdjust(curCon,&x, &y);
	    	mouseCursor.kanji = IsKanji(curCon,x, y);
			mouseCursor.x = x;
			mouseCursor.y = y;
	    	ShowCursor(&mouseCursor, TRUE);
		}
    }
}

void	PollCursor(bool wakeup)
{
    if (!curActive) return;

    if (wakeup) 
    {
		SaveScreen(FALSE);
		ShowCursor(&cursorInfo, TRUE);
		if (mInfo.has_mouse) PollMouseCursor();
		return;
    }

    if (saved) return;

    if ((saveTime > 0) && (++saverCount == saveTime)) 
    {
		ShowCursor(&cursorInfo, FALSE);
		ShowCursor(&mouseCursor, FALSE);
		SaveScreen(TRUE);
		return;
    }

    if ((cursorInfo.interval > 0) && (++cursorInfo.count == cursorInfo.interval)) 
		ToggleCursor(&cursorInfo);

    if (mInfo.has_mouse) PollMouseCursor();
}

static int      ConfigInterval(const char *confstr)
{
    cursorInfo.interval = mouseCursor.interval = atoi(confstr);
    return SUCCESS;
}

#define	COUNTER_ADDR	0x61

static int	beepCount, okBeepIO;

static int	ConfigBeep(const char *confstr)
{
    beepCount = atoi(confstr) * 10000;
#if defined(linux) && defined(CCE_TARGETARCH_I386)
    if (beepCount > 0)
	{

		okBeepIO = (ioperm(COUNTER_ADDR, 1, TRUE) == 0);
	}
#endif
    return SUCCESS;
}

void	Beep(void)
{
    if (!curActive || !okBeepIO || beepCount <= 0) return;
#if defined(linux) && defined(CCE_TARGETARCH_I386)
    PortOutb(PortInb(COUNTER_ADDR)|3, COUNTER_ADDR);
    usleep(beepCount);
    PortOutb(PortInb(COUNTER_ADDR)&0xFC, COUNTER_ADDR);
#endif
}

static int ConfigDisplay(const char *confstr)
{
    int fd = -1;
    
    if (strlen(confstr) < 10) /* buffer overflow? */
    {
        sscanf(confstr, "%9s", DisplayName);
    }

    return SUCCESS;
}

int ConfigKanjiCursor(const char *confstr)
{
    kanjiCursor = BoolConf(confstr);
    return SUCCESS;
}

int ConfigCursorTop(const char *confstr)
{
    cursorTop = atoi(confstr);
    return SUCCESS;
}

int ConfigCursorBottom(const char *confstr)
{
    cursorBtm = atoi(confstr);
    return SUCCESS;
}

int ConfigScreen(const char *confstr)
{

    sscanf(confstr, "%d %d %d", &dispInfo.tymax, &dispInfo.txmax,
           &dispInfo.linegap);

	if (dispInfo.tymax <= 0 || dispInfo.txmax <= 0 || dispInfo.linegap < 0)
		dispInfo.tymax = dispInfo.txmax = dispInfo.linegap = 0;
	
	if (fontwidth <= 0 || fontheight <= 0)
	{
		fatal("Can't find the required font for encoding!\n");
	}

	pVideoInfo = InitVideoInfo(DisplayName);
    if (pVideoInfo == NULL)
		fatal("graphics mode initialization failed!\n");

    return SUCCESS;
}

void	ConsoleInit()
{

#ifdef HAVE_TIOCWINSZ
    ioctl(0, TIOCGWINSZ, &text_win);  /* get text window size */
#endif

    DefineCap("Display", ConfigDisplay, "VGA");

    DefineCap("BeepCounter", ConfigBeep, "5");
    DefineCap("CursorInterval", ConfigInterval, "4");
    DefineCap("SaveTime", ConfigSaver, "4");

    DefineCap("Screen", ConfigScreen, "25 80 0"); /* "25 80 2"); */
    DefineCap("KanjiCursor", ConfigKanjiCursor, "On");
    DefineCap("CursorTop", ConfigCursorTop, "14");
    DefineCap("CursorBottom", ConfigCursorBottom, "15");

#if defined(__FreeBSD__) && defined(SUPPORT_FRAMEBUF)
	DefineCap("FreeBSDFrameBuffer", ConfigFreeBSDFrameBuffer, "0 0 0");
#endif
}

void	ConsoleStart(void)
{
    curActive = TRUE;

    pVideoInfo->start();

    cursorInfo.shown = mouseCursor.shown = FALSE;
    mouseCursor.sw = TRUE;
    saved = FALSE;

	if (getenv("HOME")) 
		snprintf(tmpFileName, sizeof(tmpFileName)-1, "%s/%s", getenv("HOME"), CCE_MOUSE_TMPFILE);
	else snprintf(tmpFileName, sizeof(tmpFileName)-1, "/tmp/%s%3d", CCE_MOUSE_TMPFILE, getuid());

    GraphMode();
	
}

void	ConsoleCleanup()
{

#ifdef SIGALRM
	signal(SIGALRM, SIG_DFL); /* for time */
#endif

#if defined(linux) && defined(CCE_TARGETARCH_I386)
    ioperm(COUNTER_ADDR, 1, FALSE);
#endif
    
#if defined(HAVE_VT_PROCESS_SWITCH)
	if (CCE_HANDLE_VTSWITCH)
	{
    	struct vt_mode vtm;

#ifdef SIGUSR1
    	signal(SIGUSR1, SIG_DFL);
#endif
#ifdef SIGUSR2
    	signal(SIGUSR2, SIG_DFL);
#endif

    	vtm.mode = VT_AUTO;
    	vtm.waitv = 0;
    	vtm.relsig = 0;
    	vtm.acqsig = 0;
    	ioctl(0, VT_SETMODE, &vtm);
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__SCO__)
    	ioctl(0, VT_RELDISP, 1);
#endif

	}
#endif /* HAVE_VT_PROCESS_SWITCH */

    if (curActive) 
	{
		pVideoInfo->clear(CLEAR_INPUT, DEF_BGCOLOR);  /* 0 for black */
		SetTextMode();
	}

	if (CCE_HANDLE_VTSWITCH)
	{		
#if defined(__OpenBSD__) || defined(__NetBSD__)
		int mode = WSDISPLAYIO_MODE_EMUL;  /* 0 */
		ioctl(0, WSDISPLAYIO_SMODE, &mode);
#endif

#if defined(HAVE_KDSETMODE)
    	ioctl(0, KDSETMODE, KD_TEXT);
#endif
	}

    pVideoInfo->detach(); /* will mem_unmap */

#if defined(HAVE_TIOCWINSZ) 
	ioctl(0, TIOCSWINSZ, &text_win);   
#endif
}

