summaryrefslogtreecommitdiffstats
path: root/console/pfterm.c
diff options
context:
space:
mode:
Diffstat (limited to 'console/pfterm.c')
-rw-r--r--console/pfterm.c2502
1 files changed, 2502 insertions, 0 deletions
diff --git a/console/pfterm.c b/console/pfterm.c
new file mode 100644
index 00000000..65a09a62
--- /dev/null
+++ b/console/pfterm.c
@@ -0,0 +1,2502 @@
+//////////////////////////////////////////////////////////////////////////
+// pfterm environment settings
+//////////////////////////////////////////////////////////////////////////
+#ifdef _PFTERM_TEST_MAIN
+
+#define USE_PFTERM
+#define EXP_PFTERM
+#define DBCSAWARE
+#define FT_DBCS_NOINTRESC 1
+#define DBG_TEXT_FD
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.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_SHOW_DIRTY
+// #define DBG_SHOW_REPRINT // will not work if you enable SHOW_DIRTY.
+// #define DBG_DISABLE_OPTMOVE
+// #define DBG_DISABLE_REPRINT
+
+//////////////////////////////////////////////////////////////////////////
+// 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_COLORBOLD (-2)
+#define GRAYOUT_BOLD (-1)
+#define GRAYOUT_DARK (0)
+#define GRAYOUT_NORM (1)
+#define GRAYOUT_COLORNORM (+2)
+#endif // GRAYOUT_DARK
+
+//////////////////////////////////////////////////////////////////////////
+// Typeahead
+//////////////////////////////////////////////////////////////////////////
+#ifndef TYPEAHEAD_NONE
+#define TYPEAHEAD_NONE (-1)
+#define TYPEAHEAD_STDIN (0)
+#endif // TYPEAHEAD
+
+//////////////////////////////////////////////////////////////////////////
+// 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.
+//
+// Copyright(c) 2007-2008 Hung-Te Lin <piaip@csie.ntu.edu.tw>
+// All Rights Reserved.
+// You are free to use, modify, redistribute this program in any
+// non-commercial usage (including network service).
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// 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]
+// - optimization with reprint ability [done]
+// - add auto wrap control
+//
+// 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)
+
+//////////////////////////////////////////////////////////////////////////
+// pfterm Configurations
+//////////////////////////////////////////////////////////////////////////
+
+// Prevent invalid DBCS characters
+#define FTCONF_PREVENT_INVALID_DBCS
+
+// Some terminals use current attribute to erase background
+#define FTCONF_CLEAR_SETATTR
+
+// Some terminals (NetTerm, PacketSite) have bug in bolding attribute.
+#define FTCONF_WORKAROUND_BOLD
+
+// Some terminals prefer VT100 style scrolling, including Win/DOS telnet
+#undef FTCONF_USE_ANSI_SCROLL
+#undef FTCONF_USE_VT100_SCROLL
+
+// Few poor terminals do not have relative move (ABCD).
+#undef FTCONF_USE_ANSI_RELMOVE
+
+// Handling ANSI commands with 2 parameters (ex, ESC[m;nH)
+// 2: Good terminals can accept any omit format (ESC[;nH)
+// 1: Poor terminals (eg, Win/DOS telnet) can only omit 2nd (ESC[mH)
+// 0: Very few poor terminals (eg, CrazyTerm/BBMan) cannot omit any parameters
+#define FTCONF_ANSICMD2_OMIT (0)
+
+//////////////////////////////////////////////////////////////////////////
+// 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 FTCHAR_ERASE (' ')
+#define FTATTR_ERASE (0x07)
+#define FTCHAR_BLANK (' ')
+#define FTATTR_DEFAULT (FTATTR_ERASE)
+#define FTCHAR_INVALID_DBCS ('?')
+// #define FTATTR_TRANSPARENT (0x80)
+
+#define FTDIRTY_CHAR (0x01)
+#define FTDIRTY_ATTR (0x02)
+#define FTDIRTY_DBCS (0x04)
+#define FTDIRTY_INVALID_DBCS (0x08)
+#define FTDIRTY_RAWMOVE (0x10)
+
+#define FTDBCS_SAFE (0) // standard DBCS
+#define FTDBCS_UNSAFE (1) // not on all systems (better do rawmove)
+#define FTDBCS_INVALID (2) // invalid
+
+#define FTCMD_MAXLEN (63) // max escape command sequence length
+#define FTATTR_MINCMD (16)
+
+#ifndef FTCONF_USE_ANSI_RELMOVE
+# define FTMV_COST (8) // always ESC[m;nH will costs avg 8 bytes
+#else
+# define FTMV_COST (5) // ESC[ABCD with ESC[m;nH costs avg 4+ bytes
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// 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]; // character map
+ ftattr **amap[2]; // attribute map
+ ftchar *dmap; // dirty map
+ ftchar *dcmap; // processed display map
+ 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;
+
+ // memory allocation
+ int mrows, mcols;
+
+ // raw terminal status
+ int ry, rx;
+ ftattr rattr;
+
+ // typeahead
+ char typeahead;
+
+ // escape command
+ ftchar cmd[FTCMD_MAXLEN+1];
+ int szcmd;
+
+} FlatTerm;
+
+static FlatTerm ft;
+
+#ifdef _WIN32
+ // sorry, we support only 80x24 on Windows now.
+ HANDLE hStdout;
+ COORD coordBufSize = {80, 24}, coordBufCoord = {0, 0};
+ SMALL_RECT winrect = {0, 0, 79, 23};
+ CHAR_INFO winbuf[80*24];
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+// 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 FTCHAR_ISBLANK(x) ((x) == (FTCHAR_BLANK))
+
+#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 FTDC ft.dcmap
+#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))
+
+// - 0xFF is used as telnet IAC, don't use it!
+// - 0x80 is invalid for Big5.
+#define FTDBCS_ISBADLEAD(x) ((((unsigned char)(x)) == 0x80) || (((unsigned char)(x)) == 0xFF))
+
+// 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))
+
+
+//////////////////////////////////////////////////////////////////////////
+// Flat Terminal API
+//////////////////////////////////////////////////////////////////////////
+
+//// common ncurse-like library interface
+
+// initialization
+void initscr (void);
+int resizeterm (int rows, int cols);
+int endwin (void);
+
+// attributes
+ftattr attrget (void);
+void attrset (ftattr attr);
+void attrsetfg (ftattr attr);
+void attrsetbg (ftattr attr);
+
+// cursor
+void getyx (int *y, int *x);
+void getmaxyx (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.
+
+// window control
+void newwin (int nlines, int ncols, int y, int x);
+
+// flushing
+void refresh (void); // optimized refresh
+void doupdate (void); // optimized refresh, ignore input queue
+void redrawwin (void); // invalidate whole screen
+int typeahead (int fd);// prevent refresh if input queue is not empty
+
+// scrolling
+void scroll (void); // scroll up
+void rscroll (void); // scroll down
+void scrl (int rows);
+
+// output (ncurses flavor)
+void addch (unsigned char c); // equivalent to outc()
+void addstr (const char *s); // equivalent to outs()
+void addnstr (const char *s, int n);
+
+// output (non-ncurses)
+void outc (unsigned char c);
+void outs (const char *s);
+void outns (const char *s, int n);
+void outstr (const char *str); // prepare and print a complete string.
+void addstring (const char *str); // ncurses-like of outstr().
+
+// 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);
+
+// DBCS supporting
+int fterm_DBCS_Big5(unsigned char c1, unsigned char c2);
+
+//////////////////////////////////////////////////////////////////////////
+// Flat Terminal Implementation
+//////////////////////////////////////////////////////////////////////////
+
+#define fterm_markdirty() { ft.dirty = 1; }
+
+// initialization
+
+void
+initscr(void)
+{
+#ifdef _WIN32
+ hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
+ SetConsoleScreenBufferSize(hStdout, coordBufSize);
+ SetConsoleCursorPosition(hStdout, coordBufCoord);
+#endif
+
+ memset(&ft, sizeof(ft), 0);
+ ft.attr = ft.rattr = FTATTR_DEFAULT;
+ resizeterm(FTSZ_DEFAULT_ROW, FTSZ_DEFAULT_COL);
+
+ // clear both pages
+ ft.mi = 0; clrscr();
+ ft.mi = 1; clrscr();
+ ft.mi = 0;
+
+ // typeahead
+ ft.typeahead = 1;
+
+ fterm_rawclear();
+ move(0, 0);
+}
+
+int
+endwin(void)
+{
+ int r, mi = 0;
+
+ // fterm_rawclear();
+
+ for (mi = 0; mi < 2; mi++)
+ {
+ for (r = 0; r < ft.mrows; r++)
+ {
+ free(ft.cmap[mi][r]);
+ free(ft.amap[mi][r]);
+ }
+ }
+ free(ft.dmap);
+ free(ft.dcmap);
+ memset(&ft, sizeof(ft), 0);
+ return 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);
+
+ // adjust memory only for increasing buffer
+ if (rows > ft.mrows || cols > ft.mcols)
+ {
+ for (mi = 0; mi < 2; mi++)
+ {
+ // allocate rows
+ if (rows > ft.mrows)
+ {
+ 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.mrows; i < rows; i++)
+ {
+ ft.cmap[mi][i] = (ftchar*)malloc((cols+1) * sizeof(ftchar));
+ ft.amap[mi][i] = (ftattr*)malloc((cols+1) * sizeof(ftattr));
+ // zero at end to prevent over-run
+ ft.cmap[mi][i][cols] = 0;
+ ft.amap[mi][i][cols] = 0;
+ }
+ }
+
+ // resize cols
+ if (cols > ft.mcols)
+ {
+ for (i = 0; i < ft.mrows; 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));
+ // zero at end to prevent over-run
+ ft.cmap[mi][i][cols] = 0;
+ ft.amap[mi][i][cols] = 0;
+ }
+ } else {
+ // we have to deal one case:
+ // expand x, shrink x, expand y ->
+ // now new allocated lines will have small x(col) instead of mcol.
+ // the solution is to modify mcols here, or change the malloc above
+ // to max(ft.mcols, cols).
+ ft.mcols = cols;
+ }
+ }
+
+ // adjusts dirty and display map.
+ // no need to initialize anyway.
+ if (cols > ft.mcols)
+ {
+ ft.dmap = (ftchar*) realloc(ft.dmap,
+ (cols+1) * sizeof(ftchar));
+ ft.dcmap = (ftchar*) realloc(ft.dcmap,
+ (cols+1) * sizeof(ftchar));
+ }
+
+ // do mrows/mcols assignment here, because we had 2 maps running loop above.
+ if (cols > ft.mcols) ft.mcols = cols;
+ if (rows > ft.mrows) ft.mrows = rows;
+ dirty = 1;
+ }
+
+ // clear new exposed buffer after resized
+ // because we will redawwin(), so need to change front buffer only.
+ for (i = ft.rows; i < rows; i++)
+ {
+ memset(FTCMAP[i], FTCHAR_ERASE,
+ (cols) * sizeof(ftchar));
+ memset(FTAMAP[i], FTATTR_ERASE,
+ (cols) * sizeof(ftattr));
+ }
+ if (cols > ft.cols)
+ {
+ for (i = 0; i < ft.rows; i++)
+ {
+ memset(FTCMAP[i]+ft.cols, FTCHAR_ERASE,
+ (cols-ft.cols) * sizeof(ftchar));
+ memset(FTAMAP[i]+ft.cols, FTATTR_ERASE,
+ (cols-ft.cols) * sizeof(ftattr));
+ }
+ }
+
+ if (ft.rows != rows || ft.cols != cols)
+ {
+ ft.rows = rows;
+ ft.cols = cols;
+ redrawwin();
+ }
+
+ ft.x = ranged(ft.x, 0, ft.cols-1);
+ ft.y = ranged(ft.y, 0, ft.rows-1);
+
+ 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)
+{
+ ft.x = ranged(ft.x, 0, ft.cols-1);
+ ft.y = ranged(ft.y, 0, ft.rows-1);
+ memset(FTPC, FTCHAR_ERASE, ft.cols - ft.x);
+ memset(FTPA, FTATTR_ERASE, ft.cols - ft.x);
+ fterm_markdirty();
+}
+
+void
+clrtobeg(void)
+{
+ ft.x = ranged(ft.x, 0, ft.cols-1);
+ ft.y = ranged(ft.y, 0, ft.rows-1);
+ memset(FTCROW, FTCHAR_ERASE, ft.x+1);
+ memset(FTAROW, FTATTR_ERASE, ft.x+1);
+ fterm_markdirty();
+}
+
+void
+clrcurrline(void)
+{
+ ft.y = ranged(ft.y, 0, ft.rows-1);
+ 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);
+}
+
+void newwin (int nlines, int ncols, int y, int x)
+{
+ int oy, ox;
+ // check range
+
+ x = ranged(x, 0, ft.cols-1);
+ y = ranged(y, 0, ft.rows-1);
+ ncols = ranged(x+ncols-1, x, ft.cols-1);
+ nlines = ranged(y+nlines-1, y, ft.rows-1);
+ ncols = ncols - x + 1;
+ nlines= nlines- y + 1;
+
+ if (nlines <= 0 || ncols <= 0)
+ return;
+ getyx(&oy, &ox);
+
+ while (nlines-- > 0)
+ {
+ move(y++, x);
+ // use prepare_str to erase character
+ fterm_prepare_str(ncols);
+ // memset(FTAMAP[y]+x, ft.attr, ncols);
+ // memset(FTCMAP[y]+x, FTCHAR_ERASE, ncols);
+ }
+ move(oy, ox);
+}
+
+// dirty and flushing
+
+void
+redrawwin(void)
+{
+ // flip page
+ fterm_flippage();
+ clrscr();
+
+ // clear raw terminal
+ fterm_rawclear();
+
+ // flip page again
+ fterm_flippage();
+
+ // mark for refresh.
+ fterm_markdirty();
+}
+
+int
+typeahead(int fd)
+{
+ switch(fd)
+ {
+ case TYPEAHEAD_NONE:
+ ft.typeahead = 0;
+ break;
+ case TYPEAHEAD_STDIN:
+ ft.typeahead = 1;
+ break;
+ default: // shall never reach here
+ assert(NULL);
+ break;
+ }
+ return 0;
+}
+
+void
+refresh(void)
+{
+ // prevent passive update
+ if(fterm_inbuf() && ft.typeahead)
+ return;
+ doupdate();
+}
+
+void
+doupdate(void)
+{
+ int y, x;
+ char touched = 0;
+
+ if (!ft.dirty)
+ {
+ fterm_rawcursor();
+ return;
+ }
+
+#ifdef _WIN32
+
+ assert(ft.rows == coordBufSize.Y);
+ assert(ft.cols == coordBufSize.X);
+
+ for (y = 0; y < ft.rows; y++)
+ {
+ for (x = 0; x < ft.cols; x++)
+ {
+ WORD xAttr = FTAMAP[y][x], xxAttr;
+ // w32 attribute: bit swap (0,2) and (4, 6)
+ xxAttr = xAttr & 0xAA;
+ if (xAttr & 0x01) xxAttr |= 0x04;
+ if (xAttr & 0x04) xxAttr |= 0x01;
+ if (xAttr & 0x10) xxAttr |= 0x40;
+ if (xAttr & 0x40) xxAttr |= 0x10;
+
+ winbuf[y*ft.cols + x].Attributes= xxAttr;
+ winbuf[y*ft.cols + x].Char.AsciiChar = FTCMAP[y][x];
+ }
+ }
+ WriteConsoleOutputA(hStdout, winbuf, coordBufSize, coordBufCoord, &winrect);
+
+#else // !_WIN32
+
+ // 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 and display map
+ memset(FTD, 0, ft.cols * sizeof(ftchar));
+ memcpy(FTDC,FTCMAP[y], 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
+ switch(fterm_DBCS_Big5(FTCMAP[y][x-1], FTCMAP[y][x]))
+ {
+ case FTDBCS_SAFE:
+ // safe to print
+ FTD[x-1] &= ~FTDIRTY_INVALID_DBCS;
+ FTDC[x-1] = FTCMAP[y][x-1];
+ break;
+
+ case FTDBCS_UNSAFE:
+ // ok to print, but need to rawmove.
+ FTD[x-1] &= ~FTDIRTY_INVALID_DBCS;
+ FTDC[x-1] = FTCMAP[y][x-1];
+ FTD[x-1] |= FTDIRTY_CHAR;
+ FTD[x] |= FTDIRTY_RAWMOVE;
+ break;
+
+ case FTDBCS_INVALID:
+ // only SBCS safe characters can be print.
+ if (!FTDBCS_ISSBCSPRINT(FTCMAP[y][x]))
+ {
+ FTD[x] |= FTDIRTY_INVALID_DBCS;
+ FTDC[x] = FTCHAR_INVALID_DBCS;
+ }
+ break;
+ }
+#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;
+#ifdef FTCONF_PREVENT_INVALID_DBCS
+ FTD[x] |= FTDIRTY_INVALID_DBCS;
+ FTDC[x] = FTCHAR_INVALID_DBCS;
+#endif // FTCONF_PREVENT_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_SHOW_DIRTY
+ if (!ds)
+ continue;
+#endif // DBG_SHOW_DIRTY
+
+ // Optimization: calculate ERASE section
+ // TODO ERASE takes 3 bytes (ESC [ K), so enable only if derase >= 3?
+ // TODO ERASE then print can avoid lots of space, optimize in future.
+ 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++;
+
+ len = x+1;
+
+ for (x = 0; x < len; x++)
+ {
+#ifndef DBG_SHOW_DIRTY
+ if (!FTD[x])
+ continue;
+#endif // !DBG_SHOW_DIRTY
+
+ // Optimization: re-print or move?
+#ifndef DBG_DISABLE_REPRINT
+ while (ft.ry == y && x > ft.rx && abs(x-ft.rx) < FTMV_COST)
+ {
+ int i;
+ // Special case: we may be in position of DBCS tail...
+ // Inside a refresh() loop, this will never happen.
+ // However it may occur for the first print entering refresh.
+ // So enable only space if this is the first run (!touched).
+
+ // if we don't need to change attributes,
+ // just print remaining characters
+ for (i = ft.rx; i < x; i++)
+ {
+ // if same attribute, simply accept.
+ if (FTAMAP[y][i] == ft.rattr && touched)
+ continue;
+ // XXX spaces may accept (BG=rBG),
+ // but that will also change cached attribute.
+ if (!FTCHAR_ISBLANK(FTCMAP[y][i]))
+ break;
+ if (FTATTR_GETBG(FTAMAP[y][i]) != FTATTR_GETBG(ft.rattr))
+ break;
+ }
+ if (i != x)
+ break;
+
+ // safe to print all!
+ // printf("[re-print %d chars]", i-ft.rx);
+
+#ifdef DBG_SHOW_REPRINT
+ // reverse to hint this is a re-print
+ fterm_rawattr(FTATTR_MAKE(0, 7) | FTATTR_BOLD);
+#endif // DBG_SHOW_REPRINT
+
+ for (i = ft.rx; i < x; i++)
+ {
+ fterm_rawc(FTDC[i]);
+ FTAMAP[y][i] = FTOAMAP[y][i]; // spaces may change attr...
+ ft.rx++;
+ }
+
+ break;
+ }
+#endif // !DBG_DISABLE_REPRINT
+
+ if (y != ft.ry || x != ft.rx)
+ 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_SHOW_DIRTY
+ fterm_rawattr(FTD[x] ?
+ (FTAMAP[y][x] | FTATTR_BOLD) : (FTAMAP[y][x] & ~FTATTR_BOLD));
+#else // !DBG_SHOW_DIRTY
+ fterm_rawattr(FTAMAP[y][x]);
+#endif // !DBG_SHOW_DIRTY
+
+ fterm_rawc(FTDC[x]);
+ ft.rx++;
+ touched = 1;
+
+ if (FTD[x] & FTDIRTY_RAWMOVE)
+ {
+ fterm_rawcmd2(ft.ry+1, ft.rx+1, 1, 'H');
+ }
+ }
+
+ if (derase)
+ {
+ fterm_rawmove_opt(y, len);
+ fterm_rawclreol();
+ }
+ }
+
+#endif // !_WIN32
+
+ // doing fterm_rawcursor() earlier to enable max display time
+ fterm_rawcursor();
+ fterm_dupe2bk();
+ ft.dirty = 0;
+}
+
+// cursor management
+
+void
+getyx(int *y, int *x)
+{
+ if (y)
+ *y = ft.y;
+ if (x)
+ *x = ft.x;
+}
+
+void
+getmaxyx(int *y, int *x)
+{
+ if (y)
+ *y = ft.rows;
+ if (x)
+ *x = ft.cols;
+}
+
+void
+move(int y, int x)
+{
+ ft.y = ranged(y, 0, ft.rows-1);
+ ft.x = ranged(x, 0, ft.cols-1);
+}
+
+// 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
+addch (unsigned char c)
+{
+ outc(c);
+}
+
+void
+addstr (const char *s)
+{
+ outs(s);
+}
+
+void
+addnstr(const char *s, int n)
+{
+ outns(s, n);
+}
+
+void
+addstring(const char *s)
+{
+ outstr(s);
+}
+
+void
+outs(const char *s)
+{
+ if (!s)
+ return;
+ while (*s)
+ outc(*s++);
+}
+
+void
+outns(const char *s, int n)
+{
+ if (!s)
+ return;
+ while (*s && n-- > 0)
+ 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 check 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)
+{
+ // 0xFF is invalid for most cases (even DBCS),
+ if (c == 0xFF || c == 0x00)
+ return;
+
+ 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, and erase the moved range
+ int x = ft.x;
+ if (x % 8 == 0)
+ x += 8;
+ else
+ x += (8-(x%8));
+ x = ranged(x, 0, ft.cols-1);
+ // erase the characters between
+ if (x > ft.x)
+ {
+ memset(FTCROW+ft.x, FTCHAR_ERASE, x - ft.x);
+ memset(FTAROW+ft.x, ft.attr, x-ft.x);
+ }
+ ft.x = x;
+ }
+ else if (c == '\b')
+ {
+ ft.x = ranged(ft.x-1, 0, ft.cols-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
+ {
+ assert (ft.x >= 0 && ft.x < ft.cols);
+
+ // normal characters
+ FTC = c;
+#ifdef FTATTR_TRANSPARENT
+ if (ft.attr != FTATTR_TRANSPARENT)
+#endif // FTATTR_TRANSPARENT
+ FTA = ft.attr;
+
+ ft.x++;
+ // 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 --;
+ }
+ }
+ }
+}
+
+// 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);
+}
+
+// internal core of piaip's flat-term
+
+void
+fterm_flippage (void)
+{
+ // we have only 2 pages now.
+ ft.mi = 1 - ft.mi;
+}
+
+#ifndef fterm_markdirty
+void
+fterm_markdirty (void)
+{
+ ft.dirty = 1;
+}
+#endif
+
+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_DBCS_Big5(unsigned char c1, unsigned char c2)
+{
+ // ref: http://www.cns11643.gov.tw/web/word/big5/index.html
+ // High byte: 0xA1-0xFE, 0x8E-0xA0, 0x81-0x8D
+ // Low byte: 0x40-0x7E, 0xA1-0xFE
+ // C1: 0x80-0x9F
+ if (FTDBCS_ISBADLEAD(c1))
+ return FTDBCS_INVALID;
+ if (!FTDBCS_ISTAIL(c2))
+ return FTDBCS_INVALID;
+ if (c1 >= 0x80 && c1 <= 0x9F)
+ return FTDBCS_UNSAFE;
+ return FTDBCS_SAFE;
+}
+
+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 && x > 0) // TAIL, remove word
+ x--;
+ if (dbcs == 1 && len < ft.cols) // LEAD, remove word
+ len ++;
+ len = ranged(len, 0, ft.cols);
+ len -= x;
+ if (len < 0) len = 0;
+
+ memset(FTCROW + x, FTCHAR_ERASE, len);
+ memset(FTAROW + x, ft.attr, 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 = -1, 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.
+ if (n < 1)
+ n = 1;
+ 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 partially supported (not a really attribute).
+ // 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++ = '[';
+
+ // optimization: reset as default
+ if (nattr == FTATTR_DEFAULT)
+ {
+ *s++ = 'm';
+ *s++ = 0;
+ return 1;
+ }
+
+ 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;
+
+ // we dont use "disable blink/bold" commands,
+ // so if these settings are changed then we must reset.
+ // another case is changing background to default background -
+ // better use "RESET" to override it.
+ // Same for foreground.
+ // Possible optimization: when blink/bold on, don't RESET
+ // for background change?
+ if ((oblink != blink && !blink) ||
+ (obold != bold && !bold) ||
+ (bg == FTATTR_DEFAULT_BG && obg != bg) ||
+ (fg == FTATTR_DEFAULT_FG && ofg != fg) )
+ {
+ 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';
+
+#ifdef FTCONF_WORKAROUND_BOLD
+ // Issue here:
+ // PacketSite does not understand ESC[1m. It needs ESC[1;37m
+ // NetTerm defaults bold color to yellow.
+ // As a result, we use ESC[1;37m for the case.
+ if (fg == FTATTR_DEFAULT_FG)
+ ofg = ~ofg;
+#endif // FTCONF_WORKAROUND_BOLD
+
+ }
+ 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('[');
+
+ // See FTCONF_ANSICMD2_OMIT
+ // 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)
+ {
+#if (FTCONF_ANSICMD2_OMIT >= 2)
+ if (arg1 != defval)
+#endif
+ fterm_rawnum(arg1);
+
+#if (FTCONF_ANSICMD2_OMIT >= 1)
+ if (arg2 != defval)
+#endif
+ {
+ 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");
+}
+
+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
+ // No need to do so? because we will always reset attribute...
+ // 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
+
+ // known hacks: \r = (x=0), \b=(x--), \n = (y++)
+ //
+ // Warning: any optimization here should not change displayed content,
+ // because we don't have information about content variation information
+ // (eg, invalid DBCS bytes will become special marks) here.
+ // Any hacks which will try to display data from FTCMAP should be done
+ // inside dirty-map calculation, for ex, using spaces to move right,
+ // or re-print content.
+
+#ifndef DBG_TEXT_FD
+ // x=0: the cheapest output. However not work for text mode fd output.
+ // a special case is "if we have to move y to up".
+ // and FTCONF_ANSICMD2_OMIT < 1 (cannot omit x).
+#if FTCONF_ANSICMD2_OMIT < 1
+ if (y >= ft.ry)
+#endif
+ if (adx && x == 0)
+ {
+ fterm_rawc('\r');
+ ft.rx = x = adx = 0;
+ }
+
+#endif // !DBG_TEXT_FD
+
+ // x--: compare with FTMV_COST: ESC[m;nH costs 5-8 bytes
+ if (x < ft.rx && y >= ft.ry && (adx+ady) < FTMV_COST)
+ {
+ while (adx > 0)
+ fterm_rawc('\b'), adx--;
+ ft.rx = x;
+ }
+
+ // finishing movement
+ 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)
+{
+#ifdef _WIN32
+ COORD cursor;
+ cursor.X = ft.x;
+ cursor.Y = ft.y;
+ SetConsoleCursorPosition(hStdout, cursor);
+#else
+ // fterm_rawattr(FTATTR_DEFAULT);
+ fterm_rawattr(ft.attr);
+ fterm_rawmove_opt(ft.y, ft.x);
+ fterm_rawflush();
+#endif // !_WIN32
+}
+
+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: scroll down
+ // * ESC M: scroll up
+ //
+ // Elder BBS systems works in a mixed way:
+ // \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.
+#ifdef FTCONF_USE_VT100_SCROLL
+ fterm_raws(ESC_STR "D" ESC_STR "[K"); // ESC_STR "[K");
+#else
+ fterm_raws("\n" ESC_STR "[K");
+#endif
+ } 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_COLORBOLD)
+ {
+ int x = 0;
+ for (; y < end; y++)
+ {
+ for (x = 0; x < ft.cols-1; x++)
+ FTAMAP[y][x] |= FTATTR_BOLD;
+ }
+ return;
+ }
+
+ if (level == GRAYOUT_COLORNORM)
+ {
+ int x = 0;
+ for (; y < end; y++)
+ {
+ for (x = 0; x < ft.cols-1; x++)
+ FTAMAP[y][x] &= ~(FTATTR_BLINK | FTATTR_BOLD);
+ }
+ return;
+ }
+
+ 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;
+ char *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;
+ char *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 = ranged(psb->y, 0, ft.rows-1);
+ ft.x = ranged(psb->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
+getyx_ansi(int *y, int *x)
+{
+ getyx(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
+ // if (c == ESC_CHR) putchar('*'); else
+ putchar(c);
+#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];
+ initscr();
+
+ if (argc < 2)
+ {
+#if 0
+ // DBCS test
+ char *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 (80)");
+ refresh();
+ getchar();
+#endif
+
+#if 1
+ // test resize
+ clear(); move(1, 0); outs("test resize\n");
+ doupdate(); getchar();
+ // expand X
+ resizeterm(25,200);
+ clear(); move(20, 0); clrtoeol(); outs("200x25");
+ doupdate(); getchar();
+ // resize back
+ resizeterm(25,80);
+ clear(); move(20, 0); clrtoeol(); outs("80x25");
+ doupdate(); getchar();
+ // expand Y
+ resizeterm(60,80);
+ clear(); move(20, 0); clrtoeol(); outs("80x60");
+ doupdate(); getchar();
+ // see if you crash here.
+ resizeterm(60,200);
+ clear(); move(20, 0); clrtoeol(); outs("200x60");
+ doupdate(); getchar();
+#endif
+
+#if 0
+ // test optimization
+ clear();
+ move(1, 0);
+ outs("x++ optimization test\n");
+ outs("1 2 3 4 5 6 7 8 9 0\n");
+ outs("1122233334444455555566666667777777788888888899999999990\n");
+ refresh();
+ getchar();
+
+ move(2, 0);
+ outs("1122233334444455555566666667777777788888888899999999990\n");
+ outs("1 2 3 4 5 6 7 8 9 0\n");
+ refresh();
+ getchar();
+
+ rscroll();
+ refresh();
+ getchar();
+#endif
+ } else {
+ FILE *fp = fopen(argv[1], "r");
+ int c = 0;
+
+ while (fp && (c=getc(fp)) > 0)
+ {
+ outc(c);
+ }
+ fclose(fp);
+ refresh();
+ }
+
+ endwin();
+ 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:expandtab