ansi_cio.c 11.13 KiB
#include <fcntl.h>
#include <stdarg.h>
#include <genwrap.h>
#include <threadwrap.h>
#ifdef __unix__
#include <termios.h>
#endif
#include "conio.h"
#include "ansi_cio.h"
WORD ansi_curr_attr=0x07<<8;
unsigned int ansi_rows=24;
unsigned int ansi_cols=80;
unsigned int ansi_nextchar;
int ansi_got_row=0;
int ansi_got_col=0;
int ansi_esc_delay=25;
const int ansi_tabs[10]={9,17,25,33,41,49,57,65,73,80};
const int ansi_colours[8]={0,4,2,6,1,5,3,7};
static WORD ansi_inch;
static char ansi_raw_inch;
struct termios tio_default; /* Initial term settings */
WORD *vmem;
int ansi_row=0;
int ansi_col=0;
int force_move=1;
/* Control sequence table definitions. */
typedef struct
{
char *pszSequence;
int chExtendedKey;
} tODKeySequence;
#define ANSI_KEY_UP 72<<8
#define ANSI_KEY_DOWN 80<<8
#define ANSI_KEY_RIGHT 0x4d<<8
#define ANSI_KEY_LEFT 0x4b<<8
#define ANSI_KEY_HOME 0x47<<8
#define ANSI_KEY_END 0x4f<<8
#define ANSI_KEY_F1 0x3b<<8
#define ANSI_KEY_F2 0x3c<<8
#define ANSI_KEY_F3 0x3d<<8
#define ANSI_KEY_F4 0x3e<<8
#define ANSI_KEY_F5 0x3f<<8
#define ANSI_KEY_F6 0x40<<8
#define ANSI_KEY_F7 0x41<<8
#define ANSI_KEY_F8 0x42<<8
#define ANSI_KEY_F9 0x43<<8
#define ANSI_KEY_F10 0x44<<8
#define ANSI_KEY_PGUP 0x49<<8
#define ANSI_KEY_PGDN 0x51<<8
#define ANSI_KEY_INSERT 0x52<<8
#define ANSI_KEY_DELETE 0x53<<8
tODKeySequence aKeySequences[] =
{
/* VT-52 control sequences. */
{"\033A", ANSI_KEY_UP},
{"\033B", ANSI_KEY_DOWN},
{"\033C", ANSI_KEY_RIGHT},
{"\033D", ANSI_KEY_LEFT},
{"\033H", ANSI_KEY_HOME},
{"\033K", ANSI_KEY_END},
{"\033P", ANSI_KEY_F1},
{"\033Q", ANSI_KEY_F2},
{"\033?w", ANSI_KEY_F3},
{"\033?x", ANSI_KEY_F4},
{"\033?t", ANSI_KEY_F5},
{"\033?u", ANSI_KEY_F6},
{"\033?q", ANSI_KEY_F7},
{"\033?r", ANSI_KEY_F8},
{"\033?p", ANSI_KEY_F9},
/* Control sequences common to VT-100/VT-102/VT-220/VT-320/ANSI. */
{"\033[A", ANSI_KEY_UP},
{"\033[B", ANSI_KEY_DOWN},
{"\033[C", ANSI_KEY_RIGHT},
{"\033[D", ANSI_KEY_LEFT},
{"\033[M", ANSI_KEY_PGUP},
{"\033[H\x1b[2J", ANSI_KEY_PGDN},
{"\033[H", ANSI_KEY_HOME},
{"\033[K", ANSI_KEY_END},
{"\033OP", ANSI_KEY_F1},
{"\033OQ", ANSI_KEY_F2},
{"\033OR", ANSI_KEY_F3},
{"\033OS", ANSI_KEY_F4},
/* VT-220/VT-320 specific control sequences. */
{"\033[17~", ANSI_KEY_F6},
{"\033[18~", ANSI_KEY_F7},
{"\033[19~", ANSI_KEY_F8},
{"\033[20~", ANSI_KEY_F9},
{"\033[21~", ANSI_KEY_F10},
/* ANSI-specific control sequences. */
{"\033[L", ANSI_KEY_HOME},
{"\033Ow", ANSI_KEY_F3},
{"\033Ox", ANSI_KEY_F4},
{"\033Ot", ANSI_KEY_F5},
{"\033Ou", ANSI_KEY_F6},
{"\033Oq", ANSI_KEY_F7},
{"\033Or", ANSI_KEY_F8},
{"\033Op", ANSI_KEY_F9},
/* PROCOMM-specific control sequences (non-keypad alternatives). */
{"\033OA", ANSI_KEY_UP},
{"\033OB", ANSI_KEY_DOWN},
{"\033OC", ANSI_KEY_RIGHT},
{"\033OD", ANSI_KEY_LEFT},
{"\033OH", ANSI_KEY_HOME},
{"\033OK", ANSI_KEY_END},
/* Terminator */
{"",0}
};
void ansi_sendch(char ch)
{
struct text_info ti;
if(!ch)
ch=' ';
if(ansi_row<ansi_rows-1 || (ansi_row==ansi_rows-1 && ansi_col<ansi_cols-1)) {
ansi_col++;
if(ansi_col>=ansi_cols) {
ansi_col=0;
ansi_row++;
if(ansi_row>=ansi_rows) {
ansi_col=ansi_cols-1;
ansi_row=ansi_rows-1;
}
}
fwrite(&ch,1,1,stdout);
if(ch<' ')
force_move=1;
}
else
}
void ansi_sendstr(char *str,int len)
{
if(len==-1)
len=strlen(str);
if(len) {
fwrite(str,len,1,stdout);
}
}
int ansi_puttext(int sx, int sy, int ex, int ey, unsigned char *fill)
{
int x,y;
unsigned char *out;
WORD sch;
struct text_info ti;
int attrib;
gettextinfo(&ti);
if( sx < 1
|| sy < 1
|| ex < 1
|| ey < 1
|| sx > ti.screenwidth
|| sy > ti.screenheight
|| sx > ex
|| sy > ey
|| ex > ti.screenwidth
|| ey > ti.screenheight
|| fill==NULL)
return(0);
out=fill;
attrib=ti.attribute;
for(y=sy-1;y<ey;y++) {
for(x=sx-1;x<ex;x++) {
sch=*(out++);
if(sch==27)
sch=' ';
if(sch==0)
sch=' ';
sch |= (*(out++))<<8;
if(vmem[y*ansi_cols+x]==sch)
continue;
vmem[y*ansi_cols+x]=sch;
ansi_gotoxy(x+1,y+1);
if(attrib!=sch>>8) {
textattr(sch>>8);
attrib=sch>>8;
}
ansi_sendch(sch&0xff);
}
}
gotoxy(ti.curx,ti.cury);
if(attrib!=ti.attribute)
textattr(ti.attribute);
}
int ansi_gettext(int sx, int sy, int ex, int ey, unsigned char *fill)
{
int x,y;
unsigned char *out;
WORD sch;
struct text_info ti;
gettextinfo(&ti);
if( sx < 1
|| sy < 1
|| ex < 1
|| ey < 1
|| sx > ti.screenwidth
|| sy > ti.screenheight
|| sx > ex
|| sy > ey
|| ex > ti.screenwidth
|| ey > ti.screenheight
|| fill==NULL)
return(0);
out=fill;
for(y=sy-1;y<ey;y++) {
for(x=sx-1;x<ex;x++) {
sch=vmem[y*ansi_cols+x];
*(out++)=sch & 0xff;
*(out++)=sch >> 8;
}
}
}
void ansi_textattr(unsigned char attr)
{
char str[16];
int fg,ofg;
int bg,obg;
int bl,obl;
int br,obr;
int oa;
str[0]=0;
if(ansi_curr_attr==attr<<8)
return;
bl=attr&0x80;
bg=(attr>>4)&0x7;
fg=attr&0x07;
br=attr&0x04;
oa=ansi_curr_attr>>8;
obl=oa>>7;
obg=(oa>>4)&0x7;
ofg=oa&0x07;
obr=(oa>>3)&0x01;
ansi_curr_attr=attr<<8;
strcpy(str,"\033[");
if(obl!=bl) {
if(!bl) {
strcat(str,"0;");
ofg=7;
obg=0;
obr=0;
}
else
strcat(str,"5;");
}
if(br!=obr) {
if(br)
strcat(str,"1;");
else
#if 0
strcat(str,"2;");
#else
{
strcat(str,"0;");
ofg=7;
obg=0;
}
#endif
}
if(fg!=ofg)
sprintf(str+strlen(str),"3%d;",ansi_colours[fg]);
if(bg!=obg)
sprintf(str+strlen(str),"4%d;",ansi_colours[bg]);
str[strlen(str)-1]='m';
ansi_sendstr(str,-1);
}
static void ansi_keyparse(void *par)
{
int gotesc=0;
char seq[64];
int ch;
int waited=0;
int i;
char *p;
for(;;) {
while(!ansi_raw_inch
&& (gotesc || (!gotesc && !seq[0]))) {
waited++;
if(waited>=ansi_esc_delay) {
waited=0;
gotesc=0;
}
else
SLEEP(1);
}
if(!gotesc && seq[0]) {
while(ansi_inch)
SLEEP(1);
ch=seq[0];
for(p=seq;*p;*p=*(++p));
ansi_inch=ch;
continue;
}
else {
ch=ansi_raw_inch;
ansi_raw_inch=0;
}
switch(gotesc) {
case 1: /* Escape */
waited=0;
if(strlen(seq)>=sizeof(seq)-2) {
gotesc=0;
break;
}
seq[strlen(seq)+1]=0;
seq[strlen(seq)]=ch;
if((ch<'0' || ch>'9') /* End of ESC sequence */
&& ch!=';'
&& ch!='?'
&& (strlen(seq)==2?ch != '[':1)
&& (strlen(seq)==2?ch != 'O':1)) {
for(i=0;aKeySequences[i].pszSequence[0];i++) {
if(!strcmp(seq,aKeySequences[i].pszSequence)) {
gotesc=0;
seq[0]=0;
while(ansi_inch)
SLEEP(1);
ansi_inch=aKeySequences[i].chExtendedKey;
break;
}
}
if(!aKeySequences[i].pszSequence[0])
gotesc=0;
}
break;
default:
if(ch==27) {
seq[0]=27;
seq[1]=0;
gotesc=1;
waited=0;
break;
}
while(ansi_inch)
SLEEP(1);
ansi_inch=ch;
break;
}
}
}
static void ansi_keythread(void *params)
{
_beginthread(ansi_keyparse,1024,NULL);
for(;;) {
if(!ansi_raw_inch)
ansi_raw_inch=fgetc(stdin);
else
SLEEP(1);
}
}
int ansi_kbhit(void)
{
return(ansi_inch);
}
void ansi_delay(long msec)
{
SLEEP(msec);
}
int ansi_wherey(void)
{
return(ansi_row+1);
}
int ansi_wherex(void)
{
return(ansi_col+1);
}
/* Put the character _c on the screen at the current cursor position.
* The special characters return, linefeed, bell, and backspace are handled
* properly, as is line wrap and scrolling. The cursor position is updated.
*/
int ansi_putch(unsigned char ch)
{
struct text_info ti;
WORD sch;
int i;
char buf[2];
buf[0]=ch;
buf[1]=ansi_curr_attr>>8;
gettextinfo(&ti);
switch(ch) {
case '\r':
gotoxy(1,wherey());
break;
case '\n':
if(wherey()==ti.winbottom-ti.wintop+1)
wscroll();
else
gotoxy(wherex(),wherey()+1);
break;
case '\b':
if(ansi_col>ti.winleft-1) {
buf[0]=' ';
gotoxy(wherex()-1,wherey());
puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
}
break;
case 7: /* Bell */
ansi_sendch(7);
break;
case '\t':
for(i=0;i<10;i++) {
if(ansi_tabs[i]>ansi_col+1) {
while(ansi_col+1<ansi_tabs[i]) {
putch(' ');
}
break;
}
}
if(i==10) {
putch('\r');
putch('\n');
}
break;
default:
if(wherey()==ti.winbottom-ti.wintop+1
&& wherex()==ti.winright-ti.winleft+1) {
gotoxy(1,wherey());
puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
wscroll();
}
else {
if(wherex()==ti.winright-ti.winleft+1) {
gotoxy(1,ti.cury+1);
puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
}
else {
puttext(ansi_col+1,ansi_row+1,ansi_col+1,ansi_row+1,buf);
gotoxy(ti.curx+1,ti.cury);
}
}
break;
}
return(ch);
}
void ansi_gotoxy(int x, int y)
{
struct text_info ti;
char str[16];
if(x < 1
|| x > ansi_cols
|| y < 1
|| y > ansi_rows)
return;
if(force_move) {
force_move=0;
sprintf(str,"\033[%d;%dH",y,x);
}
else {
if(x==1 && ansi_col != 0 && ansi_row<ansi_row-1) {
ansi_sendch('\r');
force_move=0;
ansi_col=0;
}
if(x==ansi_col+1) {
if(y==ansi_row+1) {
str[0]=0;
}
else {
if(y<ansi_row+1) {
if(y==ansi_row)
strcpy(str,"\033[A");
else
sprintf(str,"\033[%dA",ansi_row+1-y);
}
else {
if(y==ansi_row+2)
strcpy(str,"\033[B");
else
sprintf(str,"\033[%dB",y-ansi_row-1);
}
}
}
else {
if(y==ansi_row+1) {
if(x<ansi_col+1) {
if(x==ansi_col)
strcpy(str,"\033[D");
else
sprintf(str,"\033[%dD",ansi_col+1-x);
}
else {
if(x==ansi_col+2)
strcpy(str,"\033[C");
else
sprintf(str,"\033[%dC",x-ansi_col-1);
}
}
else {
sprintf(str,"\033[%d;%dH",y,x);
}
}
}
ansi_sendstr(str,-1);
ansi_row=y-1;
ansi_col=x-1;
}
void ansi_gettextinfo(struct text_info *info)
{
info->currmode=3;
info->screenheight=ansi_rows;
info->screenwidth=ansi_cols;
info->curx=wherex();
info->cury=wherey();
info->attribute=ansi_curr_attr>>8;
}
void ansi_setcursortype(int type)
{
switch(type) {
case _NOCURSOR:
case _SOLIDCURSOR:
default:
break;
}
}
int ansi_getch(void)
{
int ch;
while(!ansi_inch)
SLEEP(1);
ch=ansi_inch&0xff;
ansi_inch=ansi_inch>>8;
return(ch);
}
int ansi_getche(void)
{
int ch;
if(ansi_nextchar)
return(ansi_getch());
ch=ansi_getch();
if(ch)
putch(ch);
return(ch);
}
int ansi_beep(void)
{
putch(7);
return(0);
}
void ansi_textmode(int mode)
{
}
#ifdef __unix__
void ansi_fixterm(void)
{
tcsetattr(STDIN_FILENO,TCSANOW,&tio_default);
}
#endif
void ansi_initciowrap(long inmode)
{
int i;
char *init="\033[0m\033[2J\033[1;1H";
#ifdef _WIN32
_setmode(fileno(stdout),_O_BINARY);
_setmode(fileno(stdin),_O_BINARY);
setvbuf(stdout, NULL, _IONBF, 0);
#else
struct termios tio_raw;
if (isatty(STDIN_FILENO)) {
tcgetattr(STDIN_FILENO,&tio_default);
tio_raw = tio_default;
cfmakeraw(&tio_raw);
tcsetattr(STDIN_FILENO,TCSANOW,&tio_raw);
setvbuf(stdout, NULL, _IONBF, 0);
atexit(ansi_fixterm);
}
#endif
vmem=(WORD *)malloc(ansi_rows*ansi_cols*sizeof(WORD));
ansi_sendstr(init,-1);
for(i=0;i<ansi_rows*ansi_cols;i++)
vmem[i]=0x0720;
_beginthread(ansi_keythread,1024,NULL);
}