diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2007-12-23 17:27:16 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2007-12-23 17:27:16 +0800 |
commit | 7895d30695f55d4c5e80f07b677a829146dacd6e (patch) | |
tree | e4451f32e523b245902b3b1ce21e194a70bc6a9b /mbbsd | |
parent | f13cadf70e1c22bad218cd1d8df9910b1dbceeb3 (diff) | |
download | pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar.gz pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar.bz2 pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar.lz pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar.xz pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.tar.zst pttbbs-7895d30695f55d4c5e80f07b677a829146dacd6e.zip |
- pfterm: piaip's flat terminal system, aka then "Perfect Term".
"A revolution of BBS output system."
"The most important upgrade for a decade."
"pfterm is the ultimate solution!"
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@3730 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'mbbsd')
-rw-r--r-- | mbbsd/pfterm.c | 2031 |
1 files changed, 2031 insertions, 0 deletions
diff --git a/mbbsd/pfterm.c b/mbbsd/pfterm.c new file mode 100644 index 00000000..0e04a6da --- /dev/null +++ b/mbbsd/pfterm.c @@ -0,0 +1,2031 @@ +////////////////////////////////////////////////////////////////////////// +// pfterm environment settings +////////////////////////////////////////////////////////////////////////// +#ifdef _PFTERM_TEST_MAIN + +#define USE_PFTERM +#define EXP_PFTERM +#define DBCSAWARE +#define FT_DBCS_NOINTRESC 1 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#else + +#define HAVE_GRAYOUT +#include "bbs.h" + +#ifdef DBCS_NOINTRESC +// # ifdef CONVERT +// extern int bbs_convert_type; +// # define FT_DBCS_NOINTRESC ( +// (cuser.uflag & DBCS_NOINTRESC) || +// (bbs_convert_type == CONV_UTF8)) +// # else +# define FT_DBCS_NOINTRESC (cuser.uflag & DBCS_NOINTRESC) +// # endif +#else +# define FT_DBCS_NOINTRESC 0 +#endif + +#endif + +////////////////////////////////////////////////////////////////////////// +// pfterm debug settings +////////////////////////////////////////////////////////////////////////// + +// #define DBG_SHOWDIRTY +// #define DBG_DISABLE_OPTMOVE + +////////////////////////////////////////////////////////////////////////// +// pmore style ansi +// #include "ansi.h" +////////////////////////////////////////////////////////////////////////// + +#ifndef PMORE_STYLE_ANSI +#define ESC_CHR '\x1b' +#define ESC_STR "\x1b" +#define ANSI_COLOR(x) ESC_STR "[" #x "m" +#define ANSI_RESET ESC_STR "[m" +#endif // PMORE_STYLE_ANSI + +#ifndef ANSI_IS_PARAM +#define ANSI_IS_PARAM(c) (c == ';' || (c >= '0' && c <= '9')) +#endif // ANSI_IS_PARAM + +////////////////////////////////////////////////////////////////////////// +// grayout advanced control +// #include "grayout.h" +////////////////////////////////////////////////////////////////////////// +#ifndef GRAYOUT_DARK +#define GRAYOUT_BOLD (-1) +#define GRAYOUT_DARK (0) +#define GRAYOUT_NORM (1) +#endif // GRAYOUT_DARK + +////////////////////////////////////////////////////////////////////////// +// pfterm: piaip's flat terminal system, a new replacement for 'screen'. +// pfterm can also be read as "Perfect Term" +// +// piaip's new implementation of terminal system (term/screen) with dirty +// map, designed and optimized for ANSI based output. +// +// "pfterm" is "piaip's flat terminal" or "perfect term", not "PTT's term"!!! +// pfterm is designed for general maple-family BBS systems, not +// specific to any branch. +// +// Author: Hung-Te Lin (piaip), Dec. 2007. +// <piaip@csie.ntu.edu.tw> +// All Rights Reserved. +// You are free to use, modify, redistribute this program +// in any BBS style systems, or any other non-commercial usage. +// You must keep these copyright information. +// +// MAJOR IMPROVEMENTS: +// - Interpret ANSI code and maintain a virtual terminal +// - Dual buffer for dirty map and optimized output +// +// TODO AND DONE: +// - DBCS aware and prevent interrupting DBCS [done] +// - optimization with relative movement [done] +// - optimization with ENTER/clrtoeol [done] +// - ncurses-like API [done] +// - inansistr to output escaped string [done] +// - handle incomplete DBCS characters [done] +// +// DEPRECATED: +// - standout() [rework getdata() and namecomplete()] +// - talk.c (big_picture) [rework talk.c] +// +////////////////////////////////////////////////////////////////////////// +// Reference: +// http://en.wikipedia.org/wiki/ANSI_escape_code +// http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf +////////////////////////////////////////////////////////////////////////// + +// Experimental now +#if defined(EXP_PFTERM) || defined(USE_PFTERM) + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal Definition +////////////////////////////////////////////////////////////////////////// + +#define FTSZ_DEFAULT_ROW (24) +#define FTSZ_DEFAULT_COL (80) +#define FTSZ_MIN_ROW (24) +#define FTSZ_MAX_ROW (100) +#define FTSZ_MIN_COL (80) +#define FTSZ_MAX_COL (320) + +#define FTATTR_DEFAULT (0x07) +#define FTCHAR_ERASE (' ') +#define FTATTR_ERASE (0x07) +#define FTCHAR_INVALID_DBCS ('?') +// #define FTATTR_TRANSPARENT (0x80) + +#define FTDIRTY_CHAR (0x1) +#define FTDIRTY_ATTR (0x2) +#define FTDIRTY_DBCS (0x4) +#define FTDIRTY_INVALID_DBCS (0x8) + +#define FTCMD_MAXLEN (63) // max escape command sequence length +#define FTATTR_MINCMD (16) +#define FTMV_COST (5) // ESC[m;nH costs 5+ bytes + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal Data Type +////////////////////////////////////////////////////////////////////////// + +typedef unsigned char ftchar; // primitive character type +typedef unsigned char ftattr; // primitive attribute type + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal Structure +////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + ftchar **cmap[2]; + ftattr **amap[2]; + ftchar *dmap; + ftattr attr; + int rows, cols; + int y, x; + int sy,sx; // stored cursor + int mi; // map index, mi = current map and (1-mi) = old map + int dirty; + int scroll; + + // raw terminal status + int ry, rx; + ftattr rattr; + + // escape command + ftchar cmd[FTCMD_MAXLEN+1]; + int szcmd; +} FlatTerm; + +static FlatTerm ft; + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal Utility Macro +////////////////////////////////////////////////////////////////////////// + +// ftattr: 0| FG(3) | BOLD(1) | BG(3) | BLINK(1) |8 +#define FTATTR_FGSHIFT (0) +#define FTATTR_BGSHIFT (4) +#define FTATTR_GETFG(x) ((x >> FTATTR_FGSHIFT) & 0x7) +#define FTATTR_GETBG(x) ((x >> FTATTR_BGSHIFT) & 0x7) +#define FTATTR_FGMASK ((ftattr)(0x7 << FTATTR_FGSHIFT)) +#define FTATTR_BGMASK ((ftattr)(0x7 << FTATTR_BGSHIFT)) +#define FTATTR_BOLD (0x08) +#define FTATTR_BLINK (0x80) +#define FTATTR_DEFAULT_FG (FTATTR_GETFG(FTATTR_DEFAULT)) +#define FTATTR_DEFAULT_BG (FTATTR_GETBG(FTATTR_DEFAULT)) +#define FTATTR_MAKE(f,b) (((f)<<FTATTR_FGSHIFT)|((b)<<FTATTR_BGSHIFT)) + +#define FTCMAP ft.cmap[ft.mi] +#define FTAMAP ft.amap[ft.mi] +#define FTCROW FTCMAP[ft.y] +#define FTAROW FTAMAP[ft.y] +#define FTC FTCROW[ft.x] +#define FTA FTAROW[ft.x] +#define FTD ft.dmap +#define FTPC (FTCROW+ft.x) +#define FTPA (FTAROW+ft.x) +#define FTOCMAP ft.cmap[1-ft.mi] +#define FTOAMAP ft.amap[1-ft.mi] + + +// for fast checking, we use reduced range here. +// Big5: LEAD = 0x81-0xFE, TAIL = 0x40-0x7E/0xA1-0xFE +#define FTDBCS_ISLEAD(x) (((unsigned char)(x))> (0x80)) +#define FTDBCS_ISTAIL(x) (((unsigned char)(x))>=(0x40)) +// even faster: +// #define FTDBCS_ISLEAD(x) (((unsigned char)(x)) & 0x80) +// #define FTDBCS_ISTAIL(x) (((unsigned char)(x)) & ((unsigned char)~0x3F)) + +#define FTDBCS_ISSBCSPRINT(x) \ + (((unsigned char)(x))>=' ' && ((unsigned char)(x))<0x80) + +#ifndef min +#define min(x,y) (((x)<(y))?(x):(y)) +#endif + +#ifndef max +#define max(x,y) (((x)>(y))?(x):(y)) +#endif + +#ifndef abs +#define abs(x) (((x)>0)?(x):-(x)) +#endif + +#define ranged(x,vmin,vmax) (max(min(x,vmax),vmin)) + +////////////////////////////////////////////////////////////////////////// +// Special Configurations +////////////////////////////////////////////////////////////////////////// + +// Prevent invalid DBCS characters +#define FTCONF_PREVENT_INVALID_DBCS + +// Some terminals use current attribute to erase background +#define FTCONF_CLEAR_SETATTR + +// Some terminals prefer VT100 style scrolling, including Win/DOS telnet +#undef FTCONF_USE_ANSI_SCROLL + +// Few poor terminals do not have relative move (ABCD). +#undef FTCONF_USE_ANSI_RELMOVE + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal API +////////////////////////////////////////////////////////////////////////// + +//// common ncurse-like library interface + +// initialization +void initscr (void); +int resizeterm (int rows, int cols); + +// attributes +ftattr attrget (void); +void attrset (ftattr attr); +void attrsetfg (ftattr attr); +void attrsetbg (ftattr attr); + +// cursor +void getyx (int *y, int *x); +void move (int y, int x); + +// clear +void clear (void); // clrscr + move(0,0) +void clrtoeol (void); // end of line +void clrtobot (void); +// clear (non-ncurses) +void clrtoln (int ln); // clear down to ln ( excluding ln, as [y,ln) ) +void clrcurln (void); // whole line +void clrtobeg (void); // begin of line +void clrtohome (void); +void clrscr (void); // clear and keep cursor untouched +void clrregion (int r1, int r2); // clear [r1,r2], bi-directional. + +// flushing +void refresh (void); // optimized refresh +void redrawwin (void); // force refresh + +// scrolling +void scroll (void); // scroll up +void rscroll (void); // scroll down +void scrl (int rows); + +// output (non-ncurses) +void outc (unsigned char c); +void outs (const char *s); +void outstr (const char *str); // prepare and print a complete string. + +// readback +int instr (char *str); +int innstr (char *str, int n); // n includes \0 +int inansistr (char *str, int n); // n includes \0 + +// deprecated +void standout (void); +void standend (void); + +// grayout advanced control +void grayout (int y, int end, int level); + +//// flat-term internal processor + +int fterm_inbuf (void); // raw input adapter +void fterm_rawc (int c); // raw output adapter +void fterm_rawnewline(void); // raw output adapter +void fterm_rawflush (void); // raw output adapter +void fterm_raws (const char *s); +void fterm_rawnc (int c, int n); +void fterm_rawnum (int arg); +void fterm_rawcmd (int arg, int defval, char c); +void fterm_rawcmd2 (int arg1, int arg2, int defval, char c); +void fterm_rawattr (ftattr attr); // optimized changing attribute +void fterm_rawclear (void); +void fterm_rawclreol (void); +void fterm_rawhome (void); +void fterm_rawscroll (int dy); +void fterm_rawcursor (void); +void fterm_rawmove (int y, int x); +void fterm_rawmove_opt(int y, int x); +void fterm_rawmove_rel(int dy, int dx); + +int fterm_chattr (char *s, ftattr oa, ftattr na); // size(s) > FTATTR_MINCMD +void fterm_exec (void); // execute ft.cmd +void fterm_flippage (void); +void fterm_dupe2bk (void); +void fterm_markdirty (void); // mark as dirty +int fterm_strdlen (const char *s); // length of string for display +int fterm_prepare_str(int len); + +////////////////////////////////////////////////////////////////////////// +// Flat Terminal Implementation +////////////////////////////////////////////////////////////////////////// + +// initialization + +void +initscr(void) +{ + memset(&ft, sizeof(ft), 0); + resizeterm(FTSZ_DEFAULT_ROW, FTSZ_DEFAULT_COL); + ft.attr = ft.rattr = FTATTR_DEFAULT; + + // clear both pages + ft.mi = 0; clrscr(); + ft.mi = 1; clrscr(); + ft.mi = 0; + + fterm_rawclear(); + move(0, 0); +} + +int +resizeterm(int rows, int cols) +{ + int dirty = 0, mi = 0, i = 0; + + rows = ranged(rows, FTSZ_MIN_ROW, FTSZ_MAX_ROW); + cols = ranged(cols, FTSZ_MIN_COL, FTSZ_MAX_COL); + + // enlarge buffer only + + if (rows > ft.rows || cols > ft.cols) + { + for (mi = 0; mi < 2; mi++) + { + // allocate rows + if (rows > ft.rows) + { + ft.cmap[mi] = (ftchar**)realloc(ft.cmap[mi], + sizeof(ftchar*) * rows); + ft.amap[mi] = (ftattr**)realloc(ft.amap[mi], + sizeof(ftattr*) * rows); + + // allocate new columns + for (i = ft.rows; i < rows; i++) + { + ft.cmap[mi][i] = (ftchar*)malloc((cols+1) * sizeof(ftchar)); + ft.amap[mi][i] = (ftattr*)malloc((cols+1) * sizeof(ftattr)); + memset(ft.cmap[mi][i], 0, + (cols+1) * sizeof(ftchar)); + memset(ft.amap[mi][i], FTATTR_DEFAULT, + (cols+1) * sizeof(ftattr)); + } + } + + // resize cols + if (cols > ft.cols) + { + for (i = 0; i < ft.rows; i++) + { + ft.cmap[mi][i] = (ftchar*)realloc(ft.cmap[mi][i], + (cols+1) * sizeof(ftchar)); + ft.amap[mi][i] = (ftattr*)realloc(ft.amap[mi][i], + (cols+1) * sizeof(ftattr)); + memset(ft.cmap[mi][i]+ft.cols, 0, + (cols-ft.cols+1) * sizeof(ftchar)); + memset(ft.amap[mi][i]+ft.cols, FTATTR_DEFAULT, + (cols-ft.cols+1) * sizeof(ftattr)); + } + + // no need to initialize dirty map. + ft.dmap = (ftchar*) realloc(ft.dmap, + (cols+1) * sizeof(ftchar)); + } + } + dirty = 1; + } + + if (ft.rows != rows || ft.cols != cols) + { + ft.rows = rows; + ft.cols = cols; + redrawwin(); + } + + return dirty; +} + +// attributes + +ftattr +attrget(void) +{ + return ft.attr; +} + +void +attrset(ftattr attr) +{ + ft.attr = attr; +} + +void +attrsetfg(ftattr attr) +{ + ft.attr &= (~FTATTR_FGMASK); + ft.attr |= ((attr & FTATTR_FGMASK) << FTATTR_FGSHIFT); +} + +void +attrsetbg(ftattr attr) +{ + ft.attr &= (~FTATTR_BGMASK); + ft.attr |= ((attr & FTATTR_FGMASK) << FTATTR_BGSHIFT); +} + +// clear + +void +clrscr(void) +{ + int r; + for (r = 0; r < ft.rows; r++) + memset(FTCMAP[r], FTCHAR_ERASE, ft.cols * sizeof(ftchar)); + for (r = 0; r < ft.rows; r++) + memset(FTAMAP[r], FTATTR_ERASE, ft.cols * sizeof(ftattr)); + fterm_markdirty(); +} + +void +clear(void) +{ + clrscr(); + move(0,0); +} + +void +clrtoeol(void) +{ + memset(FTPC, FTCHAR_ERASE, ft.cols - ft.x); + memset(FTPA, FTATTR_ERASE, ft.cols - ft.x); + fterm_markdirty(); +} + +void +clrtobeg(void) +{ + memset(FTCROW, FTCHAR_ERASE, ft.x+1); + memset(FTAROW, FTATTR_ERASE, ft.x+1); + fterm_markdirty(); +} + +void +clrcurrline(void) +{ + memset(FTCROW, FTCHAR_ERASE, ft.cols); + memset(FTAROW, FTATTR_ERASE, ft.cols); + fterm_markdirty(); +} + +void +clrtoln(int line) +{ + if (line <= ft.y) + return; + clrregion(ft.y, line-1); +} + +void +clrregion(int r1, int r2) +{ + // bi-direction + if (r1 > r2) + { + int r = r1; + r1 = r2; r2 = r; + } + + // check r1, r2 range + r1 = ranged(r1, 0, ft.rows-1); + r2 = ranged(r2, 0, ft.rows-1); + + for (; r1 <= r2; r1++) + { + memset(FTCMAP[r1], FTCHAR_ERASE, ft.cols); + memset(FTAMAP[r1], FTATTR_ERASE, ft.cols); + } + fterm_markdirty(); +} + +void +clrtobot(void) +{ + clrtoeol(); + clrregion(ft.y+1, ft.rows-1); +} + +void +clrtohome(void) +{ + clrtobeg(); + clrregion(ft.y-1, 0); +} + + +// flushing + +void +redrawwin(void) +{ + // flip page + fterm_flippage(); + clrscr(); + + // clear raw terminal + fterm_rawclear(); + + // flip page again + fterm_flippage(); + + // now, refresh. + fterm_markdirty(); + refresh(); +} + +void +refresh(void) +{ + int y, x; + // determine page + int dirty = 0; + + if(fterm_inbuf()) + return; + + if (!ft.dirty) + { + fterm_rawcursor(); + return; + } + + // if scroll, do it first + if (ft.scroll) + fterm_rawscroll(ft.scroll); + + // calculate and optimize dirty + for (y = 0; y < ft.rows; y++) + { + int len = ft.cols, ds = 0, derase = 0; + char dbcs = 0, odbcs = 0; // 0: none, 1: lead, 2: tail + + // reset dirty map + memset(FTD, 0, ft.cols * sizeof(ftchar)); + + // first run: character diff + for (x = 0; x < len; x++) + { + // build base dirty information + if (FTCMAP[y][x] != FTOCMAP[y][x]) + FTD[x] |= FTDIRTY_CHAR, ds++; + if (FTAMAP[y][x] != FTOAMAP[y][x]) + FTD[x] |= FTDIRTY_ATTR, ds++; + + // determine DBCS status + if (dbcs == 1) + { +#ifdef FTCONF_PREVENT_INVALID_DBCS + // (1) check if lead is really valid. + // - 0xFF is used as telnet IAC, don't use it! + // - 0x80 is invalid for Big5. + // (2) check if tail is valid. + // - if not valid, only SBCS safe characters can be print. + if (FTCMAP[y][x-1] != 0xFF && + FTCMAP[y][x-1] != 0x80 && + FTDBCS_ISTAIL(FTCMAP[y][x]) ) + { + FTD[x-1] &= ~FTDIRTY_INVALID_DBCS; + } + else if (!FTDBCS_ISSBCSPRINT(FTCMAP[y][x])) + { + FTD[x] |= FTDIRTY_INVALID_DBCS; + } +#endif // FTCONF_PREVENT_INVALID_DBCS + + dbcs = 2; + // TAIL: dirty prev and me if any is dirty. + if (FTD[x] || FTD[x-1]) + { + FTD[x] |= FTDIRTY_DBCS; + FTD[x-1]|= FTDIRTY_CHAR; + } + } + else if (FTDBCS_ISLEAD(FTCMAP[y][x])) + { + // LEAD: clear dirty when tail was found. + dbcs = 1; + FTD[x] |= FTDIRTY_INVALID_DBCS; + } + else + { + // NON-DBCS + dbcs = 0; + } + + if (odbcs == 1) + { + // TAIL: dirty prev and me if any is dirty. + odbcs = 2; + if (FTD[x] || FTD[x-1]) + { + FTD[x] |= FTDIRTY_CHAR; + FTD[x-1]|= FTDIRTY_CHAR; + } + } + else if (FTDBCS_ISLEAD(FTOCMAP[y][x])) + { + // LEAD: dirty next? + odbcs = 1; + } + else + { + odbcs = 0; + } + } + +#ifndef DBG_SHOWDIRTY + if (!ds) + continue; +#endif // DBG_SHOWDIRTY + + // Optimization: calculate ERASE section + for (x = ft.cols - 1; x >= 0; x--) + if (FTCMAP[y][x] != FTCHAR_ERASE || + FTAMAP[y][x] != FTATTR_ERASE) + break; + else if (FTD[x]) + derase++; + + // Optimize by fterm_rawclreol(); + len = x+1; + + for (x = 0; x < len; x++) + { + // determine dirty flag + dirty = FTD[x]; + + // logic: each move takes at least 4+ bytes (ESC [ n C) + // each attribute change takes 5+ bytes (ESC [ xx m) + // so do move if clean region > 4 bytes or if attribute is changed. + +#ifndef DBG_SHOWDIRTY + if (!dirty) + continue; +#endif // !DBG_SHOWDIRTY + + // optimize move command + fterm_rawmove_opt(y, x); + +#ifdef DBCSAWARE + if ((FTD[x] & FTDIRTY_DBCS) && (FT_DBCS_NOINTRESC)) + { + // prevent changing attributes inside DBCS + } + else +#endif // DBCSAWARE +#ifdef DBG_SHOWDIRTY + fterm_rawattr(FTD[x] ? + (FTAMAP[y][x] | FTATTR_BOLD) : (FTAMAP[y][x] & ~FTATTR_BOLD)); +#else // !DBG_SHOWDIRTY + fterm_rawattr(FTAMAP[y][x]); +#endif // !DBG_SHOWDIRTY + +#ifdef FTCONF_PREVENT_INVALID_DBCS + if (FTD[x] & FTDIRTY_INVALID_DBCS) + fterm_rawc(FTCHAR_INVALID_DBCS); + else +#endif // FTCONF_PREVENT_INVALID_DBCS + fterm_rawc (FTCMAP[y][x]); + + ft.rx++; + } + + if (derase) + { + fterm_rawmove_opt(y, len); + fterm_rawclreol(); + } + } + + fterm_dupe2bk(); + fterm_rawcursor(); + ft.dirty = 0; +} + +// cursor management + +void +getyx(int *y, int *x) +{ + if (y) + *y = ft.y; + if (x) + *x = ft.x; +} + +void +move(int y, int x) +{ + y = ranged(y, 0, ft.rows-1); + x = ranged(x, 0, ft.cols-1); + + ft.y = y; + ft.x = x; +} + +// scrolling + +void +scrl(int rows) +{ + if (!rows) + return; + if (rows > 0) + { + for (; rows > 0; rows--) + scroll(); + } else { + for (; rows < 0; rows++) + rscroll(); + } +} + +void +scroll() +{ + // scroll up + int y; + ftchar *c0 = FTCMAP[0], *oc0 = FTOCMAP[0]; + ftattr *a0 = FTAMAP[0], *oa0 = FTOAMAP[0]; + + // prevent mixing buffered scroll up+down + if (ft.scroll < 0) + fterm_rawscroll(ft.scroll); + + // smart scroll: move pointers + for (y = 0; y < ft.rows-1; y++) + { + FTCMAP[y] = FTCMAP[y+1]; + FTAMAP[y] = FTAMAP[y+1]; + FTOCMAP[y]= FTOCMAP[y+1]; + FTOAMAP[y]= FTOAMAP[y+1]; + } + FTCMAP[y] = c0; + FTAMAP[y] = a0; + FTOCMAP[y]= oc0; + FTOAMAP[y]= oa0; + + // XXX also clear backup buffer + // must carefully consider if up then down scrolling. + fterm_flippage(); + clrregion(ft.rows-1, ft.rows-1); + fterm_flippage(); + clrregion(ft.rows-1, ft.rows-1); + + ft.scroll ++; + // fterm_markdirty(); // should be already dirty +} + +void +rscroll() +{ + // scroll down + int y; + ftchar *c0 = FTCMAP[ft.rows -1], *oc0 = FTOCMAP[ft.rows -1]; + ftattr *a0 = FTAMAP[ft.rows -1], *oa0 = FTOAMAP[ft.rows -1]; + + // prevent mixing buffered scroll up+down + if (ft.scroll > 0) + fterm_rawscroll(ft.scroll); + + // smart scroll: move pointers + for (y = ft.rows -1; y > 0; y--) + { + FTCMAP[y] = FTCMAP[y-1]; + FTAMAP[y] = FTAMAP[y-1]; + FTOCMAP[y]= FTOCMAP[y-1]; + FTOAMAP[y]= FTOAMAP[y-1]; + } + FTCMAP[y] = c0; + FTAMAP[y] = a0; + FTOCMAP[y]= oc0; + FTOAMAP[y]= oa0; + + // XXX also clear backup buffer + // must carefully consider if up then down scrolling. + fterm_flippage(); + clrregion(0, 0); + fterm_flippage(); + clrregion(0, 0); + + ft.scroll --; + // fterm_markdirty(); // should be already dirty +} + +// output + +void +outs(const char *s) +{ + if (!s) + return; + while (*s) + outc(*s++); +} + +void +outstr(const char *str) +{ + if (!str) + { + fterm_prepare_str(0); + return; + } + + // calculate real length of str (stripping escapes) + // TODO only print by the returned size + + fterm_prepare_str(fterm_strdlen(str)); + + outs(str); + + // maybe the source string itself is broken... + // basically this should be done by clients, not term library. +#if 0 + { + int isdbcs = 0; + while (*str) + { + if (isdbcs == 1) isdbcs = 2; + else if (FTDBCS_ISLEAD(*str)) isdbcs = 1; + else isdbcs = 0; + str++; + } + + if (isdbcs == 1) // incomplete string! + outs("\b?"); + } +#endif +} + +void +outc(unsigned char c) +{ + fterm_markdirty(); + if (ft.szcmd) + { + // collecting commands + ft.cmd[ft.szcmd++] = c; + + if ((ft.szcmd == 2 && c == '[') || + (ANSI_IS_PARAM(c) && ft.szcmd < FTCMD_MAXLEN)) + return; + + // process as command + fterm_exec(); + ft.szcmd = 0; + } + else if (c == ESC_CHR) + { + // start of escaped commands + ft.cmd[ft.szcmd++] = c; + } + else if (c == '\t') + { + // tab: move by 8 + int x = ft.x; + if (x % 8 == 0) + x += 8; + else + x += (8-x); + ft.x = ranged(x, 0, ft.rows-1); + } + else if (c == '\b') + { + ft.x = ranged(ft.x-1, 0, ft.rows-1); + } + else if (c == '\r' || c == '\n') + { + // new line: cursor movement, and do not print anything + // XXX old screen.c also calls clrtoeol() for newlins. + clrtoeol(); + ft.x = 0; + ft.y ++; + while (ft.y >= ft.rows) + { + // XXX scroll at next dirty? + // screen.c ignored such scroll. + // scroll(); + ft.y --; + } + } + else if (iscntrl((unsigned char)c)) + { + // unknown control characters: ignore + } + else // normal characters + { + // XXX allow x == ft.cols? + if (ft.x >= ft.cols) + { + ft.x = 0; + ft.y ++; + while (ft.y >= ft.rows) + { + // XXX scroll at next dirty? + // screen.c ignored such scroll. + // scroll(); + ft.y --; + } + } + + // normal characters + FTC = c; +#ifdef FTATTR_TRANSPARENT + if (ft.attr != FTATTR_TRANSPARENT) +#endif // FTATTR_TRANSPARENT + FTA = ft.attr; + ft.x ++; + } +} + +// readback +int +instr (char *str) +{ + int x = ft.cols -1; + *str = 0; + if (ft.y < 0 || ft.y >= ft.rows || ft.x < 0 || ft.x >= ft.cols) + return 0; + + // determine stopping location + while (x >= ft.x && FTCROW[x] == FTCHAR_ERASE) + x--; + if (x < ft.x) return 0; + x = x - ft.x + 1; + memcpy(str, FTCROW+ft.x, x); + str[x] = 0; + return x; +} + +int +innstr (char *str, int n) +{ + int on = n; + int x = ranged(ft.x + n-1, 0, ft.cols-1); + *str = 0; + n = x - ft.x+1; + if (on < 1 || ft.y < 0 || ft.y >= ft.rows || ft.x < 0 || ft.x >= ft.cols) + return 0; + + // determine stopping location + while (x >= ft.x && FTCROW[x] == FTCHAR_ERASE) + x--; + if (x < ft.x) return 0; + n = x - ft.x + 1; + if (n >= on) n = on-1; + memcpy(str, FTCROW+ft.x, n); + str[n] = 0; + return n; +} + +int +inansistr (char *str, int n) +{ + int x = ft.cols -1, i = 0, szTrail = 0; + char *ostr = str; + char cmd[FTATTR_MINCMD*2] = ""; + + ftattr a = FTATTR_DEFAULT; + *str = 0; + if (ft.y < 0 || ft.y >= ft.rows || ft.x < 0 || ft.x >= ft.cols) + return 0; + + if (n < 1) + return 0; + n--; // preserve last zero + + // determine stopping location + while (x >= ft.x && FTCROW[x] == FTCHAR_ERASE && FTAROW[x] == FTATTR_ERASE) + x--; + + // retrieve [rt.x, x] + if (x < ft.x) return 0; + + // preserve some bytes if last attribute is not FTATTR_DEFAULT + for (i = ft.x; n > szTrail && i <= x; i++) + { + *str = 0; + + if (a != FTAROW[i]) + { + fterm_chattr(cmd, a, FTAROW[i]); + a = FTAROW[i]; + + if (a != FTATTR_DEFAULT) + szTrail = 3; // ESC [ m + else + szTrail = 0; + + if (strlen(cmd) >= n-szTrail) + break; + + strcpy(str, cmd); + n -= strlen(cmd); + str += strlen(cmd); + } + + // n should > szTrail + *str ++ = FTCROW[i]; + n--; + } + + if (szTrail && n >= szTrail) + { + *str++ = ESC_CHR; *str++ = '['; *str++ = 'm'; + } + + *str = 0; + return (str - ostr); +} + +// core of piaip's flat-term + +void +fterm_flippage (void) +{ + // we have only 2 pages now. + ft.mi = 1 - ft.mi; +} + +void +fterm_markdirty (void) +{ + ft.dirty = 1; +} + +void fterm_dupe2bk(void) +{ + int r = 0; + + for (r = 0; r < ft.rows; r++) + { + memcpy(FTOCMAP[r], FTCMAP[r], ft.cols * sizeof(ftchar)); + memcpy(FTOAMAP[r], FTAMAP[r], ft.cols * sizeof(ftattr)); + } +} + +int +fterm_prepare_str(int len) +{ + // clear and make (cursor, +len) as DBCS-ready. + int x = ranged(ft.x, 0, ft.cols-1); + int y = ranged(ft.y, 0, ft.rows-1); + int dbcs = 0, sdbcs = 0; + + // TODO what if x+len is outside range? + + // check if (x,y) is in valid range + if (x != ft.x || y != ft.y) + return -1; + + len = ranged(x+len, x, ft.cols); + + for (x = 0; x < len; x++) + { + // determine DBCS status + if (dbcs == 1) + dbcs = 2; // TAIL + else if (FTDBCS_ISLEAD(FTCROW[x])) + dbcs = 1; // LEAD + else + dbcs = 0; + if (x == ft.x) sdbcs = dbcs; + } + + x = ft.x; + // fix start and end + if(sdbcs == 2) // TAIL, remove word + x--; + if (dbcs == 1) // LEAD, remove word + len ++; + len = ranged(len, 0, ft.rows); + len -= x; + + memset(FTCROW + x, FTCHAR_ERASE, len); + return len; +} + + +void +fterm_exec(void) +{ + ftchar cmd = ft.cmd[ft.szcmd-1]; + char *p = (char*)ft.cmd + 2; // ESC [ + int n = -1, x, y; + + ft.cmd[ft.szcmd] = 0; + + if (isdigit(*p)) + { + n = atoi(p); + + while (*p && isdigit(*p)) + p++; + + if (*p == ';') + p++; + // p points to next param now + } + + switch(cmd) + { + // Cursor Movement + + case 'A': // CUU: CSI n A + case 'B': // CUD: CSI n B + case 'C': // CUF: CSI n C + case 'D': // CUB: CSI n D + // Moves the cursor n (default 1) cells in the given direction. + // If the cursor is already at the edge of the screen, this has no effect. + if (n < 1) + n = 1; + getyx(&y, &x); + if (cmd == 'A') { y -= n; } + else if (cmd == 'B') { y += n; } + else if (cmd == 'C') { x += n; } + else if (cmd == 'D') { x -= n; } + move(y, x); + break; + + case 'E': // CNL: CSI n E + case 'F': // CPL: CSI n F + // Moves cursor to beginning of the line + // n (default 1) lines up/down (next/previous line). + if (n < 1) + n = 1; + getyx(&y, &x); + if (cmd == 'E') { y -= n; } + else if (cmd == 'F') { y += n; } + move(y, 0); + break; + + case 'G': // CHA: CSI n G + // Moves the cursor to column n. + getyx(&y, &x); + move(y, n-1); + break; + + case 'H': // CUP: CSI n ; m H + case 'f': // HVP: CSI n ; m f + // Moves the cursor to row n, column m. + // The values are 1-based, and default to 1 (top left corner) if omitted. + // A sequence such as CSI ;5H is a synonym for CSI 1;5H as well as + // CSI 17;H is the same as CSI 17H and CSI 17;1H + y = n; + if (y >= 0 && isdigit(*p)) + x = atoi((char*)p); + if (y < 0) y = 1; + if (x < 0) x = 1; + move(y-1, x-1); + break; + + // Clear + + case 'J': // ED: CSI n J + // Clears part of the screen. + // If n is zero (or missing), clear from cursor to end of screen. + // If n is one, clear from cursor to beginning of the screen. + // If n is two, clear entire screen + if (n == 0 || n < 0) + clrtobot(); + else if (n == 1) + clrtohome(); + else if (n == 2) + { + clrregion(0, ft.rows-1); + } + break; + + case 'K': // EL: CSI n K + // Erases part of the line. + // If n is zero (or missing), clear from cursor to the end of the line. + // If n is one, clear from cursor to beginning of the line. + // If n is two, clear entire line. Cursor position does not change. + if (n == 0 || n < 0) + clrtoeol(); + else if (n == 1) + clrtobeg(); + else if (n == 2) + clrcurrline(); + break; + + case 's': // SCP: CSI s + // Saves the cursor position. + getyx(&ft.sy, &ft.sx); + break; + + case 'u': // RCP: CSI u + // Restores the cursor position. + move(ft.sy, ft.sx); + break; + + case 'S': // SU: CSI n S + // Scroll whole page up by n (default 1) lines. + // New lines are added at the bottom. + if (n < 1) + n = 1; + scrl(n); + break; + + case 'T': // SD: CSI n T + // Scroll whole page down by n (default 1) lines. + // New lines are added at the top. + if (n < 1) + n = 1; + scrl(-n); + break; + + case 'm': // SGR: CSI n [;k] m + // Sets SGR (Select Graphic Rendition) parameters. + // After CSI can be zero or more parameters separated with ;. + // With no parameters, CSI m is treated as CSI 0 m (reset / normal), + // which is typical of most of the ANSI codes. + // --------------------------------------------------------- + // SGR implementation: + // SGR 0 (reset/normal) is supported. + // SGR 1 (intensity: bold) is supported. + // SGR 2 (intensity: faint) is not supported. + // SGR 3 (italic: on) is not supported. (converted to inverse?) + // SGR 4 (underline: single) is not supported. + // SGR 5 (blink: slow) is supported. + // SGR 6 (blink: rapid) is converted to (blink: slow) + // SGR 7 (image: negative) is supported. + // SGR 8 (conceal) is not supported. + // SGR 21(underline: double) is not supported. + // SGR 22(intensity: normal) is supported. + // SGR 24(underline: none) is not supported. + // SGR 25(blink: off) is supported. + // SGR 27(image: positive) is not supported. + // SGR 28(reveal) is not supported. + // SGR 30-37 (FG) is supported. + // SGR 39 (FG-reset) is supported. + // SGR 40-47 (BG) is supported. + // SGR 49 (BG-reset) is supported. + if (n == -1) // first param + n = 0; + while (n > -1) + { + if (n >= 30 && n <= 37) + { + // set foreground + attrsetfg(n - 30); + } + else if (n >= 40 && n <= 47) + { + // set background + attrsetbg(n - 40); + } + else switch(n) + { + case 0: + attrset(FTATTR_DEFAULT); + break; + case 1: + attrset(attrget() | FTATTR_BOLD); + break; + case 22: + attrset(attrget() & ~FTATTR_BOLD); + break; + case 5: + case 6: + attrset(attrget() | FTATTR_BLINK); + break; + case 25: + attrset(attrget() & ~FTATTR_BLINK); + break; + case 3: + case 7: + { + ftattr a = attrget(); + attrsetfg(FTATTR_GETBG(a)); + attrsetbg(FTATTR_GETFG(a)); + } + break; + case 39: + attrsetfg(FTATTR_DEFAULT_FG); + break; + case 49: + attrsetbg(FTATTR_DEFAULT_BG); + break; + } + + // parse next command + n = -1; + if (*p == ';') + { + n = 0; + p++; + } + else if (isdigit(*p)) + { + n = atoi(p); + while (isdigit(*p)) p++; + if (*p == ';') + p++; + } + } + break; + + default: // unknown command. + break; + } +} + +int +fterm_chattr(char *s, ftattr oattr, ftattr nattr) +{ + ftattr + fg, bg, bold, blink, + ofg, obg, obold, oblink; + char lead = 1; + + if (oattr == nattr) + return 0; + + *s++ = ESC_CHR; + *s++ = '['; + + fg = FTATTR_GETFG(nattr); + bg = FTATTR_GETBG(nattr); + bold = (nattr & FTATTR_BOLD) ? 1 : 0; + blink = (nattr & FTATTR_BLINK)? 1 : 0; + + ofg = FTATTR_GETFG(oattr); + obg = FTATTR_GETBG(oattr); + obold = (oattr & FTATTR_BOLD) ? 1 : 0; + oblink = (oattr & FTATTR_BLINK)? 1 : 0; + + if ((oblink != blink && !blink) || + (obold != bold && !bold) ) + { + if (lead) lead = 0; else *s++ = ';'; + *s++ = '0'; + ofg = FTATTR_DEFAULT_FG; + obg = FTATTR_DEFAULT_BG; + obold = 0; oblink = 0; + } + + if (bold && !obold) + { + if (lead) lead = 0; else *s++ = ';'; + *s++ = '1'; + } + if (blink && !oblink) + { + if (lead) lead = 0; else *s++ = ';'; + *s++ = '5'; // XXX 5(slow) or 6(fast)? + } + if (ofg != fg) + { + if (lead) lead = 0; else *s++ = ';'; + *s++ = '3'; + *s++ = '0' + fg; + } + if (obg != bg) + { + if (lead) lead = 0; else *s++ = ';'; + *s++ = '4'; + *s++ = '0' + bg; + } + *s++ = 'm'; + *s++ = 0; + return 1; +} + +int +fterm_strdlen(const char *s) +{ + char ansi = 0; + int sz = 0; + + // the logic should match outc(). + + while (s && *s) + { + if (!ansi) // ansi == 0 + { + switch(*s) + { + case ESC_CHR: + ansi++; + break; + + case '\r': + case '\n': + break; + + case '\b': + if (sz) sz--; + break; + + case '\t': + // XXX how to deal with this? + sz ++; + break; + + default: + if (!iscntrl((unsigned char)*s)) + sz++; + break; + } + } + else if (ansi == 1) + { + if (*s == '[') + ansi++; + else + ansi = 0; + } + else if (!ANSI_IS_PARAM(*s)) // ansi == 2 + { + // TODO outc() take max to FTCMD_MAXLEN now... + ansi = 0; + } + s++; + } + return sz; +} + +void +fterm_rawattr(ftattr rattr) +{ + static char cmd[FTATTR_MINCMD*2]; + if (!fterm_chattr(cmd, ft.rattr, rattr)) + return; + + fterm_raws(cmd); + ft.rattr = rattr; +} + +void +fterm_rawnum(int arg) +{ + if (arg < 0 || arg > 99) + { + // complex. use printf. + char sarg[16]; // max int + sprintf(sarg, "%d", arg); + fterm_raws(sarg); + } else if (arg < 10) { + // 0 .. 10 + fterm_rawc('0' + arg); + } else { + fterm_rawc('0' + arg/10); + fterm_rawc('0' + arg%10); + } +} +void +fterm_rawcmd(int arg, int defval, char c) +{ + fterm_rawc(ESC_CHR); + fterm_rawc('['); + if (arg != defval) + fterm_rawnum(arg); + fterm_rawc(c); +} + +void +fterm_rawcmd2(int arg1, int arg2, int defval, char c) +{ + fterm_rawc(ESC_CHR); + fterm_rawc('['); + + // XXX Win/DOS telnet does now accept omitting first value + // ESC[nX and ESC[n;X works, but ESC[;mX does not work. + if (arg1 != defval || arg2 != defval) + { + fterm_rawnum(arg1); + if (arg2 != defval) + { + fterm_rawc(';'); + fterm_rawnum(arg2); + } + } + fterm_rawc(c); +} + +void +fterm_rawclear(void) +{ + fterm_rawhome(); + // ED: CSI n J, 0 = cursor to bottom, 2 = whole + fterm_raws(ESC_STR "[2J"); + fterm_rawflush(); +} + +void +fterm_rawclreol(void) +{ +#ifdef FTCONF_CLEAR_SETATTR + ftattr oattr = ft.rattr; + // XXX If we skip with "backround only" here, future updating + // may get wrong attributes. Or not? (consider DBCS...) + // if (FTATTR_GETBG(oattr) != FTATTR_GETBG(FTATTR_ERASE)) + fterm_rawattr(FTATTR_ERASE); +#endif + + // EL: CSI n K, n = 0 + fterm_raws(ESC_STR "[K"); + +#ifdef FTCONF_CLEAR_SETATTR + fterm_rawattr(oattr); +#endif +} + +void +fterm_rawhome(void) +{ + // CUP: CSI n ; m H + fterm_raws(ESC_STR "[H"); + ft.rx = ft.ry = 0; +} + +void +fterm_rawmove_rel(int dy, int dx) +{ +#ifndef FTCONF_USE_ANSI_RELMOVE + // Old BBS system does not output relative moves (ESC[ABCD) . + // Poor terminals (ex, pcman-1.0.5-FF20.xpi) + // do not support relmoves + fterm_rawmove(ft.ry + dy, ft.rx + dx); +#else + if (!dx) + { + int y = ranged(dy + ft.ry, 0, ft.rows-1); + dy = y - ft.ry; + if (!dy) + return; + + fterm_rawcmd(abs(dy), 1, dy < 0 ? 'A' : 'B'); + ft.ry = y; + } + else if (!dy) + { + int x = ranged(dx + ft.rx, 0, ft.cols-1); + dx = x - ft.rx; + if (!dx) + return; + + fterm_rawcmd(abs(dx), 1, dx < 0 ? 'D' : 'C'); + ft.rx = x; + } + else + { + // (dy, dx) are given - use fterm_move. + fterm_rawmove(ft.ry + dy, ft.rx + dx); + } +#endif +} + +void +fterm_rawmove(int y, int x) +{ + y = ranged(y, 0, ft.rows-1); + x = ranged(x, 0, ft.cols-1); + + if (y == ft.ry && x == ft.rx) + return; + + // CUP: CSI n ; m H + fterm_rawcmd2(y+1, x+1, 1, 'H'); + + ft.ry = y; + ft.rx = x; +} + +void +fterm_rawmove_opt(int y, int x) +{ + // optimized move + int ady = abs(y-ft.ry), adx=abs(x-ft.rx); + + if (!adx && !ady) + return; + +#ifdef DBG_DISABLE_OPTMOVE + return fterm_rawmove(y, x); +#endif + + // hacks: \r = (x=0), \b=(x--), \n = (y++) + if (adx && x == 0) + { + fterm_rawc('\r'); + ft.rx = x = adx = 0; + } + + // FTMV_COST: ESC[m;nH costs 5 bytes + if (x < ft.rx && y >= ft.ry && (adx+ady) < FTMV_COST) + { + while (adx > 0) + fterm_rawc('\b'), adx--; + ft.rx = x; + } + + // if move right and same attributes between, use space. + if (x > ft.rx && (adx+ady) < FTMV_COST) + { + int i = 0; + for (; i < adx; i++) + { + // warning: count space only, to preven DBCS issue. + if (FTCMAP[ft.ry][ft.rx+i] != ' ' || + FTAMAP[ft.ry][ft.rx+i] != ft.rattr) + break; + } + + if (i == adx) + { + // safe to use spaces + fterm_rawnc(' ', adx); + x = ft.rx += adx; + adx = 0; + } + } + + // finishing + if (y > ft.ry && ady < FTMV_COST && adx == 0) + { + while (ft.ry++ < y) + fterm_rawc('\n'); + ft.ry = y; + } + else if (ady && adx) + { + fterm_rawmove(y, x); + } + else if (ady) + { + fterm_rawmove_rel(y-ft.ry, 0); + } + else if (adx) + { + fterm_rawmove_rel(0, x-ft.rx); + } +} + +void +fterm_rawcursor(void) +{ + fterm_rawattr(ft.attr); + fterm_rawmove_opt(ft.y, ft.x); + fterm_rawflush(); +} + +void +fterm_rawscroll (int dy) +{ +#ifdef FTCONF_USE_ANSI_SCROLL + // SU: CSI n S (up) + // SD: CSI n T (down) + + char cmd = (dy > 0) ? 'S' : 'T'; + int ady = abs(dy); + if (ady == 0) + return; + if (ady >= ft.rows) ady = ft.rows; + fterm_rawcmd(ady, 1, cmd); + ft.scroll -= dy; + +#else + // VT100 flavor: + // * ESC D: cursor down - at bottom of region, scroll up + // * ESC M: cursor up - at top of region, scroll down + // Elder BBS systems do \n at (rows-1) as scroll() + // and ESC-M at(0) as rscoll(). + // SCP: CSI s / RCP: CSI u + // Warning: cannot use SCP/RCP here, because on Win/DOS telnet + // the position will change after scrolling (ex, (25,0)->(24,0). + // + // Since Scroll does not happen very often, let's relax and not + // optimize these commands here... + + int ady = abs(dy); + if (ady == 0) + return; + if (ady >= ft.rows) ady = ft.rows; + + // we are not going to preserve (rx,ry) + // so don't use fterm_move*. + if (dy > 0) + fterm_rawcmd2(ft.rows, 1, 1, 'H'); + else + fterm_rawcmd2(1, 1, 1, 'H'); + + for (; ady > 0; ady--) + { + if (dy >0) + { + // Win/DOS telnet may have extra text in new line, + // because of the IME line. + fterm_raws("\n" ESC_STR "[K"); + } else { + fterm_raws(ESC_STR "M"); // ESC_STR "[K"); + } + } + + // Do not use optimized move here, because in poor terminals + // the coordinates are already out of sync. + fterm_rawcmd2(ft.ry+1, ft.rx+1, 1, 'H'); + ft.scroll -= dy; +#endif +} + +void +fterm_raws(const char *s) +{ + while (*s) + fterm_rawc(*s++); +} + +void +fterm_rawnc(int c, int n) +{ + while (n-- > 0) + fterm_rawc(c); +} + +////////////////////////////////////////////////////////////////////////// +// grayout advanced control +////////////////////////////////////////////////////////////////////////// +void +grayout(int y, int end, int level) +{ + char grattr = FTATTR_DEFAULT; + + y = ranged(y, 0, ft.rows-1); + end = ranged(end, 0, ft.rows-1); + + if (level == GRAYOUT_BOLD) + { + grattr |= FTATTR_BOLD; + } + else if (level == GRAYOUT_DARK) + { + grattr = FTATTR_MAKE(0,0); + grattr |= FTATTR_BOLD; + } + else if (level == GRAYOUT_NORM) + { + // normal text + } + else + { + // not supported yet + } + + for (; y <= end; y++) + { + memset(FTAMAP[y], grattr, ft.cols); + } +} + +////////////////////////////////////////////////////////////////////////// +// deprecated api +////////////////////////////////////////////////////////////////////////// + +void +standout(void) +{ + outs(ANSI_COLOR(7)); +} + +void +standend(void) +{ + outs(ANSI_RESET); +} + +#ifndef _PFTERM_TEST_MAIN +void +scr_dump(screen_backup_t *psb) +{ + int y = 0; + void *p = NULL; + + psb->row= ft.rows; + psb->col= ft.cols; + psb->y = ft.y; + psb->x = ft.x; + p = psb->raw_memory = + malloc (ft.rows * ft.cols * (sizeof(ftchar) + sizeof(ftattr))); + + for (y = 0; y < ft.rows; y++) + { + memcpy(p, FTCMAP[y], ft.cols * sizeof(ftchar)); + p += ft.cols * sizeof(ftchar); + memcpy(p, FTAMAP[y], ft.cols * sizeof(ftattr)); + p += ft.cols * sizeof(ftattr); + } +} + +void +scr_restore(const screen_backup_t *psb) +{ + int y = 0; + void *p = NULL; + int c = ft.cols, r = ft.rows; + if (!psb || !psb->raw_memory) + return; + + p = psb->raw_memory; + c = ranged(c, 0, psb->col); + r = ranged(r, 0, psb->row); + + ft.y = psb->y; + ft.x = psb->x; + ft.y = ranged(ft.y, 0, ft.rows-1); + ft.x = ranged(ft.x, 0, ft.cols-1); + clrscr(); + + for (y = 0; y < r; y++) + { + memcpy(FTCMAP[y], p, c * sizeof(ftchar)); + p += psb->col * sizeof(ftchar); + memcpy(FTAMAP[y], p, c * sizeof(ftattr)); + p += psb->col * sizeof(ftattr); + } + + free(psb->raw_memory); + ft.dirty = 1; + refresh(); +} + +void +move_ansi(int y, int x) +{ + move(y, x); +} + +void +region_scroll_up(int top, int bottom) +{ + int i; + ftchar *c0; + ftattr *a0; + + // logic same with old screen.c + if (top > bottom) { + i = top; + top = bottom; + bottom = i; + } + if (top < 0 || bottom >= ft.rows) + return; + + c0 = FTCMAP[top]; + a0 = FTAMAP[top]; + + for (i = top; i < bottom; i++) + { + FTCMAP[i] = FTCMAP[i+1]; + FTAMAP[i] = FTAMAP[i+1]; + } + FTCMAP[bottom] = c0; + FTAMAP[bottom] = a0; + + clrregion(bottom, bottom); + fterm_markdirty(); +} + +#endif + +////////////////////////////////////////////////////////////////////////// +// adapter +////////////////////////////////////////////////////////////////////////// + +int +fterm_inbuf(void) +{ +#ifdef _PFTERM_TEST_MAIN + return 0; +#else + return num_in_buf(); +#endif +} + +void +fterm_rawc(int c) +{ +#ifdef _PFTERM_TEST_MAIN + putc(c, stdout); +#else + ochar(c); +#endif +} + +void +fterm_rawnewline(void) +{ +#ifdef _PFTERM_TEST_MAIN + putchar('\n'); +#else + ochar('\r'); + ochar('\n'); +#endif +} + +void +fterm_rawflush(void) +{ +#ifdef _PFTERM_TEST_MAIN + fflush(stdout); +#else + oflush(); +#endif +} + +////////////////////////////////////////////////////////////////////////// +// test +////////////////////////////////////////////////////////////////////////// + +#ifdef _PFTERM_TEST_MAIN +int main(int argc, char* argv[]) +{ + char buf[512]; + char *a1 = NULL; + initscr(); + + if (argc < 2) + { + // dbcs test + a1 = ANSI_COLOR(1;33) "測試" ANSI_COLOR(34) "中文" + ANSI_COLOR(7) "測試" ANSI_RESET "測試" + "測試a" ANSI_RESET "\n"; + + outstr(a1); + move(0, 2); + outstr("中文1"); + outstr(ANSI_COLOR(1;33)"中文2"); + outstr(" 中\x85"); + outstr("okok herer\x8a"); + + move(0, 8); + inansistr(buf, sizeof(buf)-1); + + move(3,5); outs(ANSI_RESET "(From inansistr:) "); outs(buf); + move(7, 3); + sprintf(buf, "strlen()=%d\n", fterm_strdlen(a1)); + outstr(buf); + refresh(); + getchar(); + outs(ANSI_COLOR(1;33) "test " ANSI_COLOR(34) "x" + ANSI_RESET "te" ANSI_COLOR(43;0;1;35) " st" + ANSI_COLOR(0) "testx\n"); + refresh(); + getchar(); + + clear(); + outs("中文中文中文中文中文中文中文中文中文中文中文中文"); + move(0, 0); + outs(" this\xFF (ff)is te.(80 tail)->\x80 "); + refresh(); + getchar(); + + refresh(); + getchar(); + + rscroll(); + refresh(); + getchar(); + } else { + FILE *fp = fopen(argv[1], "r"); + int c = 0; + + while (fp && (c=getc(fp)) > 0) + { + outc(c); + } + refresh(); + } + + printf("\ncomplete. enter to exit.\n"); + getchar(); + return 0; +} +#endif // _PFTERM_TEST_MAIN + +#endif // defined(EXP_PFTERM) || defined(USE_PFTERM) + +// vim:ts=4:sw=4 |