/* * 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 */ #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) { char *tmp = (char *)head; do { *tmp &= 0x7f; tmp++; } while( --n ); } static inline void llatch(void *head, int n) { int *tmp = (int *)head; n /= 4; do { *tmp &= 0x7f7f7f7f; tmp++; } while( --n ); } 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); }