/*
 * KON - Kanji ON Linux Console - Copyright (C) 1992, 1993 MAEDA Atusi
 * (mad@math.keio.ac.jp) Takashi MANABE (manabe@tut.ac.jp)
 * 
 * KON 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.
 * 
 * KON 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.
 */
/*
 * modified for Big5Con by Hung-Chi Chu <hcchu@r350.ee.ntu.edu.tw>
 */

#include	"big5con.h"

struct dispInfo dInfo;
struct cursorInfo cInfo;
struct videoInfo vInfo;
static struct cursorInfo mouseCursor;

static bool     textClear;

static int      textHead, scrollLine;
static u_int    textSize;
static u_char  *textBuff, *attrBuff, *flagBuff;
u_char         *cin_textBuff, *cin_attrBuff, *cin_flagBuff;

static int      saveTime, saverCount;
static bool     saved;
static bool     useHardScroll;
static          volatile bool busy;	/* TRUE iff updating screen */
static          volatile bool release;	/* delayed VC switch flag */

static void     ShowCursor(struct cursorInfo *, bool);

static void     LeaveVC(int);
static void     EnterVC(int);

/*
  
  flagBuff:
  |      7|      6|      5|4||3|2|1|0|
  |CLEAN_S|LATCH_2|LATCH_1| ||<----->|
  |0=latch|  byte2|  byte1| ||   LANG|

  */

#define	KON_TMP_FILE	"/tmp/.big5contmp"

static
inline void 
blatch(void *head, int n)
{

    __asm__         volatile ("\t clc\n"
			                      "1:\n"
			                      "\t andb %%bl, (%%eax)\n"
			                      "\t incl %%eax\n"
			                      "\t loop 1b\n"
			      :               "=bl" (head), "=c"(n)
		      :               "eax"((long)head), "0"(0x7F), "1"(n));
}

static
inline void 
llatch(void *head, int n)
{

    __asm__         volatile ("\t clc\n"
			                      "1:\n"
			                      "\t andl %%ebx, (%%eax)\n"
			                      "\t addl $4, %%eax\n"
			                      "\t loop 1b\n"
			      :               "=ebx" (head), "=c"(n)
	   :               "eax"((long)head), "0"(0x7F7F7F7F), "1"(n >> 2));
}

static inline u_int 
TextAddress(u_int x, u_int y)
{
    return (textHead + x + y * dInfo.glineByte) % textSize;
}

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

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

void 
euctosjis(ch, cl)
    u_char         *ch, *cl;
{
    u_char          nh, nl;

    nh = ((*ch - 0x21) >> 1) + 0x81;
    if (nh > 0x9F)
	nh += 0x40;
    if (*ch & 1) {
	nl = *cl + 0x1F;
	if (*cl > 0x5F)
	    nl++;
    } else
	nl = *cl + 0x7E;
    *cl = nl;
    *ch = nh;
}

void 
TextDeleteChar(int n)
{
    u_int           addr, dx;

    addr = TextAddress(con.x, con.y);
    dx = dInfo.glineByte - con.x - n;

    bmove(textBuff + addr, textBuff + addr + n, dx);
    bmove(attrBuff + addr, attrBuff + addr + n, dx);
    blatch(flagBuff + addr, dx);

    addr = TextAddress(dInfo.glineByte - n, con.y);
    bzero2(textBuff + addr, n);
    bzero2(attrBuff + addr, n);
    bzero2(flagBuff + addr, n);
}

void 
TextInsertChar(int n)
{
    u_int           addr, dx;

    addr = TextAddress(dInfo.txmax, con.y);
    dx = dInfo.glineByte - con.x - n;

    brmove(textBuff + addr, textBuff + addr - n, dx);
    brmove(attrBuff + addr, attrBuff + addr - n, dx);
    blatch(flagBuff + addr, dx);

    addr = TextAddress(con.x, con.y);
    blatch(flagBuff + addr + n, dx);
    bzero2(textBuff + addr, n);
    bzero2(attrBuff + addr, n);
    bzero2(flagBuff + addr, n);
}

void 
TextRefresh(void)
{
    u_int           fnt, i;
    u_char          ch, ch2, fc, bc;

    busy = TRUE;
    if (!con.active) {
	busy = FALSE;
	return;
    }
    ShowCursor(&cInfo, FALSE);
    ShowCursor(&mouseCursor, FALSE);
    if (textClear)
	vInfo.clear_all();
    if (useHardScroll) {
	if (scrollLine > 0)
	    vInfo.hard_scroll_up(scrollLine);
	else if (scrollLine < 0)
	    vInfo.hard_scroll_down(-scrollLine);
	scrollLine = 0;
    }
    textClear = FALSE;
    i = TextAddress(0, dInfo.tymax);
    bmove(textBuff + i, cin_textBuff, dInfo.glineByte);
    bmove(attrBuff + i, cin_attrBuff, dInfo.glineByte);
    bmove(flagBuff + i, cin_flagBuff, dInfo.glineByte);
    for (i = 0; i < textSize; i++) {
	if (*(flagBuff + i) & CLEAN_S)
	    continue;		/* already clean */
	vInfo.set_address(i);
	fc = *(attrBuff + i);
	bc = *(attrBuff + i) >> 4;
	ch = *(textBuff + i);
	*(flagBuff + i) |= CLEAN_S;
	if (*(flagBuff + i) & CODEIS_1) {
	    dbFReg = &fDRegs[*(flagBuff + i) & LANG_CODE];
	    i++;
	    *(flagBuff + i) |= CLEAN_S;
	    ch2 = *(textBuff + i);
	    fnt = dbFReg->addr(ch, ch2);
	    if (con.ins)
		TextInsertChar(2);
	    if (fnt < dbFReg->size)
		vInfo.wput(dbFReg->bitmap + fnt, fc, bc);
	} else {
	    sbFReg = &fSRegs[*(flagBuff + i) & LANG_CODE];
	    if (con.ins)
		TextInsertChar(1);
	    vInfo.sput(ch ? sbFReg->bitmap + (ch << 4) : 0, fc, bc);
	}
    }
    i = TextAddress(0, dInfo.tymax);
    bzero(textBuff + i, dInfo.glineByte);
    bzero(attrBuff + i, dInfo.glineByte);
    bzero(flagBuff + i, dInfo.glineByte);
    cInfo.kanji = IsKanji(con.x, con.y);
    vInfo.set_cursor_address(&cInfo, con.x, con.y);
    ShowCursor(&cInfo, TRUE);
    busy = FALSE;
    if (release)
	LeaveVC(SIGUSR1);
}

static struct winsize text_win;

static void 
SetTextMode(void)
{
    ShowCursor(&cInfo, FALSE);
    vInfo.clear_all();
    vInfo.text_mode();
    ioctl(0, KDSETMODE, KD_TEXT);
    ioctl(0, TIOCCONS, NULL);
}

void 
TextMode(void)
{
    struct vt_mode  vtm;

    ioctl(0, KDSKBMODE, K_XLATE);
#if defined(__FreeBSD__)
    signal(SIGUSR1, SIG_IGN);
    signal(SIGUSR2, SIG_IGN);
#else   /* linux */
    signal(SIGUSR1, SIG_DFL);
    signal(SIGUSR2, SIG_DFL);
#endif
    vtm.mode = VT_AUTO;
    vtm.waitv = 0;
#if defined(__FreeBSD__)
    vtm.relsig = SIGUSR1;
    vtm.acqsig = SIGUSR2;
    vtm.frsig = SIGUSR1;
#else   /* linux */
    vtm.relsig = 0;
    vtm.acqsig = 0;
#endif
    ioctl(0, VT_SETMODE, &vtm);
#if defined(__FreeBSD__)
    ioctl(0, VT_RELDISP, 1);
#endif
    con.text_mode = TRUE;
    SetTextMode();
    ioctl(masterPty, TIOCSWINSZ, &text_win);
}

void 
GraphMode(void)
{
    struct winsize  win;
    struct vt_mode  vtm;

    ioctl(0, KDSKBMODE, K_RAW);
    con.text_mode = FALSE;
    ioctl(0, KDSETMODE, KD_GRAPHICS);
#if defined(__FreeBSD__)
    ioctl(0, VT_RELDISP, VT_ACKACQ);
#endif
    signal(SIGUSR1, LeaveVC);
    signal(SIGUSR2, EnterVC);
    vtm.mode = VT_PROCESS;
    vtm.waitv = 0;
    vtm.relsig = SIGUSR1;
    vtm.acqsig = SIGUSR2;
#if defined(__FreeBSD__)
    vtm.frsig = SIGUSR1;	/* not implemented, just a hack */
#endif
    ioctl(0, VT_SETMODE, &vtm);
    vInfo.graph_mode();
    if (useHardScroll)
	vInfo.set_start_address();

    win.ws_row = dInfo.tymax;	
      /* Note: con.ymax may be changed by application */
    win.ws_col = dInfo.txmax + 1;
    win.ws_xpixel = win.ws_ypixel = 0;
    ioctl(masterPty, TIOCSWINSZ, &win);
    ioctl(masterPty, TIOCCONS, NULL);

    llatch(flagBuff, textSize);
    textClear = TRUE;
    TextRefresh();
}

static
void 
LeaveVC(int signum)
{

    signal(SIGUSR1, LeaveVC);	/* should use sigaction()? */
    if (busy) {
	release = TRUE;
	return;
    }
    release = FALSE;
    con.active = FALSE;
    SetTextMode();
#ifdef	HAS_MOUSE
    if (mInfo.has_mouse) {
	MouseResetRfd(mouseFd);
	MouseCleanup();
    }
#endif
    ioctl(0, VT_RELDISP, 1);
}

static
void 
EnterVC(int signum)
{
    signal(SIGUSR2, EnterVC);
    if (!con.active) {
	con.active = TRUE;
	GraphMode();
	signal(SIGUSR2, EnterVC);
#ifdef	HAS_MOUSE
	if (mInfo.has_mouse) {
	    MouseStart();
	    MouseSetRfd(mouseFd);
	}
#endif
    }
}

static
void 
TextScrollUp(int line)
{
    int             oldhead, len;

    oldhead = textHead;
    textHead += line * dInfo.glineByte;
    if (textHead > textSize) {
	textHead -= textSize;
	len = textSize - oldhead;
	if (textHead) {
	    lzero(textBuff, textHead);
	    lzero(attrBuff, textHead);
	    lzero(flagBuff, textHead);
	}
    } else
	len = textHead - oldhead;
    lzero(textBuff + oldhead, len);
    lzero(attrBuff + oldhead, len);
    lzero(flagBuff + oldhead, len);
}

static
void 
TextScrollDown(int line)
{
    int             oldhead, len;

    oldhead = textHead;
    textHead -= line * dInfo.glineByte;
    if (textHead < 0) {
	textHead += textSize;
	if (oldhead) {
	    lzero(textBuff, oldhead);
	    lzero(attrBuff, oldhead);
	    lzero(flagBuff, oldhead);
	}
	len = textSize - textHead;
    } else
	len = oldhead - textHead;
    lzero(textBuff + textHead, len);
    lzero(attrBuff + textHead, len);
    lzero(flagBuff + textHead, len);
}

void 
TextWput1(u_char ch)
{
    u_int           addr;

    addr = TextAddress(con.x, con.y);
    *(attrBuff + addr) = con.fcol | (con.bcol << 4);
    *(textBuff + addr) = ch;
    *(flagBuff + addr) |= LATCH_T1;
}

void 
TextWput2(u_char ch)
{
    u_int           addr;
    u_char         *p;

    addr = TextAddress(con.x, con.y);
    *(textBuff + addr) = ch;
    *(p = flagBuff + addr) = LATCH_2;
    *(p - 1) = con.db | LATCH_T1;
}

void 
TextSput(u_char ch)
{
    u_int           addr;
    u_char         *p;

    addr = TextAddress(con.x, con.y);
    p = flagBuff + addr;
    if ((*p & LATCH_2) && (con.x > 0) && (*(p - 1) & LATCH_1)) {
	*(p - 1) = LATCH_S | con.sb;
    }
    if ((*p & LATCH_1) && (con.x < con.xmax) && (*(p + 1) & LATCH_2)) {
	*(p + 1) = LATCH_S | con.sb;
    }
    *p = LATCH_S | con.sb;
    *(attrBuff + addr) = con.fcol | (con.bcol << 4);
    *(textBuff + addr) = ch;
}

void 
TextClearAll(void)
{
    lzero(textBuff, textSize);
    lzero(attrBuff, textSize);
    lzero(flagBuff, textSize);
    mInfo.sw = 0;
    textClear = TRUE;
}

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

    switch (mode) {
    case 1:
	len = con.x;
	break;
    case 2:
	len = dInfo.glineByte;
	break;
    default:
	x = con.x;
	len = dInfo.glineByte - con.x;
	break;
    }
    addr = TextAddress(x, con.y);
    bzero2(textBuff + addr, len);
    bzero2(attrBuff + addr, len);
    bzero2(flagBuff + addr, len);	/* needless to latch */
}

void 
TextClearEos(u_char mode)
{
    u_int           addr, len, y;

    if (mode == 2) {
	TextClearAll();
	return;
    }
    switch (mode) {
    case 1:
	for (y = 0; y < con.y; y++) {
	    addr = TextAddress(0, y);
	    lzero(textBuff + addr, dInfo.glineByte);
	    lzero(attrBuff + addr, dInfo.glineByte);
	    lzero(flagBuff + addr, dInfo.glineByte);	/* needless to latch */
	}
	addr = TextAddress(0, con.y);
	bzero2(textBuff + addr, con.x);
	bzero2(attrBuff + addr, con.x);
	bzero2(flagBuff + addr, con.x);	/* needless to latch */
	break;
    default:
	for (y = con.y + 1; y <= dInfo.tymax; y++) {
	    addr = TextAddress(0, y);
	    lzero(textBuff + addr, dInfo.glineByte);
	    lzero(attrBuff + addr, dInfo.glineByte);
	    lzero(flagBuff + addr, dInfo.glineByte);	/* needless to latch */
	}
	addr = TextAddress(con.x, con.y);
	len = dInfo.glineByte - con.x;
	bzero2(textBuff + addr, len);
	bzero2(attrBuff + addr, len);
	bzero2(flagBuff + addr, len);	/* needless to latch */
	break;
    }
}

static
void 
TextClearBand(u_int top, u_int btm)
{
    u_int           y, addr;

    for (y = top; y <= btm; y++) {
	addr = TextAddress(0, y);
	lzero(textBuff + addr, dInfo.glineByte);
	lzero(attrBuff + addr, dInfo.glineByte);
	lzero(flagBuff + addr, dInfo.glineByte);	/* needless to latch */
    }
}

void 
TextMoveDown(int top, int btm, int line)
{
    u_int           n, src, dst;

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

void 
TextMoveUp(int top, int btm, int line)
{
    u_int           n, src, dst;

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

void 
ScrollUp(int line)
{
    if (useHardScroll && !con.soft) {
	TextScrollUp(line);
	scrollLine += line;
    } else
	TextMoveUp(con.ymin, con.ymax - 1, line);
}

void 
ScrollDown(int line)
{
    if (useHardScroll && !con.soft) {
	TextScrollDown(line);
	scrollLine -= line;
    } else
	TextMoveDown(con.ymin, con.ymax - 1, line);
}

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

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

    KanjiAdjust(&fx, &fy);
    KanjiAdjust(&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 = dInfo.txmax, y = fy; y <= ty; y++) {
	if (y == ty)
	    xx = tx;
	from = TextAddress(fx, y);
	to = TextAddress(xx, y);
	if (flagBuff[from] & CODEIS_2)
	    /* 2nd byte of kanji */
	    from--;
	for (x = from; x <= to; x++) {
	    if (!textBuff[x])
		continue;
	    fc = attrBuff[x];
	    bc = fc >> 4;
	    bc2 = (bc & 8) | (fc & 7);
	    fc2 = (fc & 8) | (bc & 7);
	    attrBuff[x] = fc2 | (bc2 << 4);
	    flagBuff[x] &= ~CLEAN_S;
	}
	fx = 0;
    }
}

void 
TextCopy(int fx, int fy, int tx, int ty)
{
    int             fd;
    u_int           from, to, y, swp, xx, x;
    u_char          ch, ch2;

    unlink(KON_TMP_FILE);
    if ((fd = open(KON_TMP_FILE, O_WRONLY | O_CREAT, 0600)) < 0)
	return;

    KanjiAdjust(&fx, &fy);
    KanjiAdjust(&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 = dInfo.txmax, y = fy; y <= ty; y++) {
	if (y == ty)
	    xx = tx;
	from = TextAddress(fx, y);
	if (flagBuff[from] & CODEIS_2)
	    /* 2nd byte of kanji */
	    from--;
	to = TextAddress(xx, y);
	for (x = to; x >= from; x--)
	    if (textBuff[x] > ' ')
		break;
	to = x;
	for (x = from; x <= to; x++) {
	    ch = textBuff[x];
	    if (!ch)
		ch = ' ';
	    if (flagBuff[x] & CODEIS_1) {
		x++;
		ch2 = textBuff[x];
		switch (lInfo.sc) {
		case CODE_EUC:
		    ch2 |= 0x80;
		    ch |= 0x80;
		    break;
		case CODE_SJIS:
		    jistosjis(ch2, ch);
		    break;
		}
		write(fd, &ch, 1);
		write(fd, &ch2, 1); 
	    } else
		write(fd, &ch, 1);
	}
	if (y < ty) {
	    ch = '\n';
	    write(fd, &ch, 1);
	}
	fx = 0;
    }
    close(fd);
}

void 
TextPaste(void)
{
    u_char          ch;
    int             fd;

    if ((fd = open(KON_TMP_FILE, O_RDONLY)) < 0)
	return;
    while (read(fd, &ch, 1) == 1)
	write(masterPty, &ch, 1);
    close(fd);
}

/* Cursor related routines. */

static void 
ToggleCursor(struct cursorInfo * c)
{
    c->count = 0;
    if (con.text_mode)
	return;
    c->shown = !c->shown;
    vInfo.cursor(c);
}

static void 
ShowCursor(struct cursorInfo * c, bool show)
{
    if (!con.active || !c->sw)
	return;
    if (c->shown != show)
	ToggleCursor(c);
}

static void 
SaveScreen(bool save)
{
    if (saved != save) {
	saved = save;
	vInfo.screen_saver(save);
    }
    saverCount = 0;
}

static void 
PollMouseCursor(void)
{
    ShowCursor(&mouseCursor, FALSE);
    if (mInfo.sw > 0) {
	--mInfo.sw;
	if (cInfo.shown) {
	    int             x = mInfo.x, y = mInfo.y;

	    KanjiAdjust(&x, &y);
	    mouseCursor.kanji = IsKanji(x, y);
	    vInfo.set_cursor_address(&mouseCursor, x, y);
	    ShowCursor(&mouseCursor, TRUE);
	}
    }
}

/* Called when some action was over, or every 1/10 sec when idle. */

void 
PollCursor(bool wakeup)
{
    if (!con.active)
	return;
    if (wakeup) {
	SaveScreen(FALSE);
	ShowCursor(&cInfo, TRUE);
	PollMouseCursor();
	return;
    }
    /* Idle. */
    if (saved)
	return;
    if ((saveTime > 0) && (++saverCount == saveTime)) {
	ShowCursor(&cInfo, FALSE);
	ShowCursor(&mouseCursor, FALSE);
	SaveScreen(TRUE);
	return;
    }
    if ((cInfo.interval > 0) && (++cInfo.count == cInfo.interval)) {
	ToggleCursor(&cInfo);
    }
    if (mInfo.has_mouse) {
	PollMouseCursor();
    }
}

/* Configuration routines. */

extern int      SvgaSetVideoType(struct videoInfo *, const char *);
extern int      VgaFmSetVideoType(struct videoInfo *, const char *);
extern int      S3SetVideoType(struct videoInfo *, const char *);
extern int      J31SXSetVideoType(struct videoInfo *, const char *);

static struct videoconf {
    const char     *name;
    int             (*set) (struct videoInfo *, const char *);
}               videos[] = {
    {
	"VGA", SvgaSetVideoType
    },
    {
	"VGAFM", VgaFmSetVideoType
    },
    {
	"EGA", SvgaSetVideoType
    },
    {
	"SVGA", SvgaSetVideoType
    },
#ifdef	HAS_S3
    {
	"S3", S3SetVideoType
    },
#endif
#ifdef	HAS_J31SX
    {
	"J3100SX", J31SXSetVideoType
    },
#endif
    {
	NULL, NULL
    }
};

static int 
ConfigHardScroll(const char *confstr)
{
    bool            value = BoolConf(confstr);
    useHardScroll = value;
    if (value) {
	message("hardware scroll mode.\r\n");
    }
    return SUCCESS;
}

static char    *videoName;

static int 
ConfigDisplay(const char *config)
{
    struct videoconf *v;
    char            name[MAX_COLS];

    sscanf(config, "%s", name);
    for (v = videos; v->name != NULL; v++) {
	if (strcasecmp(name, v->name) == 0) {
	    config = strchr(config, '\n');
	    if (config == NULL) {
		error("invalid entry for %s\r\n", videoName);
		return FAILURE;
	    }
	    if (v->set(&vInfo, config) == FAILURE)
		return FAILURE;
	    message("video type `%s' selected\r\n", name);
	    if (vInfo.has_hard_scroll) {
		DefineCap("HardScroll", ConfigHardScroll, "On");
	    } else
		useHardScroll = FALSE;
	    return SUCCESS;
	}
    }
    error("unknown video type `%s'\r\n", name);
    return FAILURE;
}

/* Beep routines. */

#define	COUNTER_ADDR	0x61

static int      beepCount;

static int 
ConfigBeep(const char *confstr)
{
#if defined(linux)
    beepCount = atoi(confstr) * 10000;
    if (beepCount > 0)
	ioperm(COUNTER_ADDR, 1, TRUE);
#endif
#if defined(__FreeBSD__)
    beepCount = atoi(confstr) * 10;
#endif
    return SUCCESS;
}

#define BELL_PITCH 800

void 
Beep(void)
{
#if defined(linux)
    if (!con.active || beepCount <= 0) return;
    PortOutb(PortInb(COUNTER_ADDR)|3, COUNTER_ADDR);
    usleep(beepCount);
    PortOutb(PortInb(COUNTER_ADDR)&0xFC, COUNTER_ADDR);
#endif
#if defined(__FreeBSD__)
    if(beepCount <= 0) return;
    ioctl(fileno(stdout), KDMKTONE, (BELL_PITCH & 0xffff) | 
          ((beepCount & 0xffff) << 16)); 
#endif
}

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

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

/* Initialize routine. */

void 
ConsoleInit(const char *video)
{
    videoName = strdup(video);
    DefineCap(videoName, ConfigDisplay, "VGA\n640 680 768 800 480 491 493 525\n1\n79 24\n");
    DefineCap("BeepCounter", ConfigBeep, "1");
    DefineCap("CursorInterval", ConfigInterval, "4");
    DefineCap("SaveTime", ConfigSaver, "5");
}

void 
ConsoleStart(int argc, const char *argv[])
{
    /* What to do if calloc failed? */
    textBuff = (u_char *) calloc(dInfo.glineByte, dInfo.tymax + 1);
    attrBuff = (u_char *) calloc(dInfo.glineByte, dInfo.tymax + 1);
    flagBuff = (u_char *) calloc(dInfo.glineByte, dInfo.tymax + 1);

    /* add by b5c */
    cin_textBuff = (u_char *) calloc(dInfo.glineByte, 1);
    cin_attrBuff = (u_char *) calloc(dInfo.glineByte, 1);
    cin_flagBuff = (u_char *) calloc(dInfo.glineByte, 1);
    memset(cin_attrBuff, NormalColor, dInfo.glineByte);
    cin_init(argc, argv);

    textSize = dInfo.glineByte * (dInfo.tymax + 1);
    ioctl(0, KDSETMODE, KD_GRAPHICS);
    ioctl(0, TIOCGWINSZ, &text_win);
    vInfo.init();
    cInfo.shown = mouseCursor.shown = FALSE;
    mouseCursor.sw = TRUE;
    saved = FALSE;
    GraphMode();
}

void 
ConsoleCleanup(void)
{
    scrollLine = textHead = 0;
    vInfo.detatch();
    SafeFree((void **)&textBuff);
    SafeFree((void **)&attrBuff);
    SafeFree((void **)&flagBuff);
#if defined(linux)
    ioperm(COUNTER_ADDR, 1, FALSE);
#endif

    SafeFree((void **)&videoName);
}

int 
isPrevW2()
{
    u_int           addr;
    if (con.x <= 0)
	return 0;
    addr = TextAddress(con.x - 1, con.y);
    return (*(flagBuff + addr) & LATCH_2);
}

int 
isNextW2()
{
    u_int           addr;
    if (con.x >= con.xmax)
	return 0;
    addr = TextAddress(con.x + 1, con.y);
    return (*(flagBuff + addr) & LATCH_2);
}

int 
isPrevWT1()
{
    u_int           addr;
    addr = TextAddress(con.x - 1, con.y);
    return (*(flagBuff + addr) & LATCH_T1);
}
