/* C code for a GUI VT100 terminal Emulator Originally developed for the Mac in June 1996 by Orion Sky Lawlor. Released into the public domain by the author in 1997. This is just a hunk of a larger Macintosh program. API calls are Mac OS (version 2.1 and greater) API calls. Substantially comment-free. Developed as throwaway code. key: Procedure(params); <- MacOS API call (capital first letter) procedure(params); <- my procedures (lowercase first letter). */ typedef struct winData { Rect frame; Boolean frontmost; long sig; Boolean terminal; short charWidth,lineHeight; short pos,line;//x,y short maxPos,maxLine;//maximum x,y Rect hiliteR; short scrollStart,scrollEnd; char inVT52Y,savedPos,savedLine,savedAttrib; char reverseScreen,relative,wraparound,ANSI,attrib; char inEsc;//number of characters in char escSeq[14];//escape sequence. short inRef,outRef; char *tab; } winData; //External procedure: void send(winData *o,char sendThis); //Send one character to the remote machine //Entry points: void termInit(winData *o);//fill out a terminal record void termChar(WindowPtr win,char c);//Process a recieved character void termClick(winData *o,Point where);//handle a mouse click in the VT window //Private routines Rect getPos(winData *o,short pos,short line); void checkPos(winData *o,short *pos,short *line); void termReset(winData *o); void termLF(winData *o); void termEsc(winData *o); void termInit(winData *o) { o->terminal=true; o->tab=(char *)NewPtr(256); termReset(o); } #define abs(x) ((x<0)?(-x):x) void termClick(winData *o,Point where) { short x=where.h/o->charWidth,y=where.v/o->lineHeight; short dirx=o->pos-x,diry=o->line-y; short numx=abs(dirx),numy=abs(diry); for (numy;numy>0;numy--) { send(o,0x1B); if (diry>0) //move up send(o,'A'); else //move down send(o,'B'); } for (numx;numx>0;numx--) { send(o,0x1B); if (dirx>0) //move left send(o,'D'); else //move right send(o,'C'); } } void termReset(winData *o) { short i; for (i=0;i<256;i++) o->tab[i]=0; for (i=0;i<(256/4);i++) o->tab[i<<2]=1; TextFont(4); TextSize(9); o->charWidth=CharWidth(' '); o->lineHeight=12; o->pos=o->line=0; o->hiliteR=getPos(o,o->pos,o->line); o->reverseScreen=o->relative=o->wraparound=o->ANSI=o->attrib=0; o->scrollStart=0; o->scrollEnd=1000; o->inVT52Y=o->inEsc=0;//number of characters in // char escSequence[7];//escape sequence. } void termLF(winData *o) { if (o->line>=o->maxLine) { Rect r=o->frame; r.top=o->scrollStart*o->lineHeight; r.bottom=o->scrollEnd*o->lineHeight; if (r.bottom>o->frame.bottom) r.bottom=o->frame.bottom; o->line=o->maxLine; RgnHandle scrollRgn=NewRgn(); ScrollRect(&o->frame,0,-o->lineHeight,scrollRgn); DisposeRgn(scrollRgn); } } Boolean isAlpha(char c); Boolean isAlpha(char c) {return ((c<='z')&&(c>='a'))||((c<='Z')&&(c>='A'));} Boolean isNum(char c); Boolean isNum(char c) {return (c<='9')&&(c>='0');} short num(char *c); short num(char *c) { short i=0,out=0; for (i=0;isNum(c[i]);i++) { out*=10; out+=c[i]-'0'; } return out; } short num2(char *c); short num2(char *c) { short i=0; while (c[i]!=';') i++; i++; return num(&c[i]); } char toUpper(char c); char toUpper(char c) { if ((c<='z')&&(c>='a')) return c-'a'+'A'; return c; } void termEsc(winData *o) { Rect r; short i; short oldInEsc=o->inEsc; char c=o->escSeq[oldInEsc-2]; o->inEsc=0; char first=o->escSeq[0]; if ((first=='=')||(first=='>')||(first=='1')||(first=='2')) {}//don't care about enter/exit keypad, or graphics anything. else if (first=='<') o->ANSI=1;//enter ANSI mode... else if (first=='7') //save Cursor position+attribs: {o->savedPos=o->pos;o->savedLine=o->line;o->savedAttrib=o->attrib;} else if (first=='8') //restore Cursor position+attribs: {o->pos=o->savedPos;o->line=o->savedLine;o->attrib=o->savedAttrib;} else if ((first=='#')&&(oldInEsc==3)) {}//ignore double/single height codes else if (((first=='(')||(first==')'))&&(oldInEsc==3)) {}//ignore alternate character set selection. else if (isAlpha(c)) {//might be the end... find out. if (first=='[') { char val; switch(c) { case 'A':o->line-=num(&o->escSeq[1]);break; case 'B':o->line+=num(&o->escSeq[1]);break; case 'C':o->pos+=num(&o->escSeq[1]);break; case 'c'://What are you? send(o,0x1b);send(o,'[');send(o,'?');send(o,'1');send(o,';'); send(o,'2');//I'm a base terminal (noOptions), with advanced video send(o,'c'); break; case 'D':o->pos-=num(&o->escSeq[1]);break; case 'H': case 'f': if (o->relative) { o->line+=num(&o->escSeq[1])-1; o->pos+=num2(&o->escSeq[1])-1; } else { o->line=num(&o->escSeq[1])-1; o->pos=num2(&o->escSeq[1])-1; } break; case 'h':case 'l'://sets or clears a value: val=(c=='h'); if (o->escSeq[1]=='?') { switch(o->escSeq[2]) { case '2':o->ANSI&=val;//turn ANSI off only. case '5':o->reverseScreen=val;break;//inverse video on/off case '6':o->relative=val;break;//relative moves on/off case '7':o->wraparound=val;break;//edge screen wrap on/off } } break; case 'g': if (o->escSeq[1]!='3') o->tab[o->pos]=0;//clear one tab else for (i=0;i<256;i++) o->tab[i]=0;//clear all tabs break; case 'J': r=o->frame; if (o->escSeq[1]!='2') {r.top=o->hiliteR.bottom;} EraseRect(&r); break; case 'K': r=o->hiliteR; r.right=o->frame.right; if (o->escSeq[1]=='2') {r.left=0;} else if (o->escSeq[1]=='1') {r.left=0;r.right=o->hiliteR.right;} EraseRect(&r); break; case 'm': if (o->escSeq[1]=='1') o->attrib|=bold; else if (o->escSeq[1]=='4') o->attrib|=underline; else if (o->escSeq[1]=='7'||o->escSeq[1]=='5') o->attrib|=outline; else if (o->escSeq[1]=='m') o->attrib=0; else o->attrib=bold|outline; break; case 'n'://report: send(o,0x1B); send(o,'['); switch(o->escSeq[1]) { case '6'://cursor position report send(o,o->line+'0');send(o,';');send(o,o->pos+'0');send(o,'R'); break; case '5'://are you OK? send(o,'c');//yes. break; } break; case 'L': case 'M': case 'P': SysBeep(20); //edit commands: insert one line if [L, insert n lines for [nL. //delete [M one char from cursor position, delete [nM n lines down. //delete [P one char from cursor position,[nP delete n characters. //don't care yet. break; case 'p': termReset(o); break; case 'q': break;//Set cursor LED's:don't care. case 'r'://scrolling region select: o->scrollStart=num(&o->escSeq[1])-1; o->scrollEnd=num2(&o->escSeq[1]); break; default: interrupt(); } } else { switch(c) { case 'A': o->line--; break; case 'B': o->line++; break; case 'C': o->pos++; break; case 'c': termReset(o);break;//reset terminal case 'D': if (o->ANSI) { o->line++; termLF(o); } else o->pos--; break; case 'E': o->pos=0;o->line++;termLF(o);break; case 'F': break;//enter special graphics set (don't care) case 'G': break;//enter ASCII character set (don't care) case 'H': if (!o->ANSI) o->line=o->pos=0; else o->tab[o->pos]=1; break; case 'I': o->line--; break; case 'J': r=o->frame;r.bottom=o->hiliteR.bottom; EraseRect(&r);break; case 'K': r=o->hiliteR;r.right=o->frame.right; EraseRect(&r);break; case 'M': o->line--;break; case 'Y': o->inVT52Y=2;break; case 'Z': send(o,0x1B);send(o,'/');send(o,'Z');break; default: interrupt(); } } } else o->inEsc=oldInEsc;//otherwise, keep listening. } void termChar(const WindowPtr win, char c) { register winData *o=obj(win); if (o->inVT52Y) { if (o->inVT52Y==2) { o->inVT52Y--; o->line=c-'0'; } else if (o->inVT52Y==1) { o->inVT52Y--; o->pos=c-'0'; } } else if (o->inEsc!=0) {//escape sequence: if (o->inEsc-1>13) o->inEsc=0; o->escSeq[o->inEsc-1]=c; o->inEsc++; termEsc(o); } else if (c==0x1B)//escape o->inEsc=1; else if (c==0x09)//tab {//find next tab stop short i; for (i=o->pos;!o->tab[i];i++) o->pos=i; } else if ((c==0x03)||(c==0x0A))//newline or LF { o->line++; termLF(o); } else if (c==0x0d)//CR o->pos=0; else if ((c==0x08)||(c==0x10))//backspace or delete { o->pos--; checkPos(o,&o->pos,&o->line); o->hiliteR=getPos(o,o->pos,o->line); EraseRect(&o->hiliteR); } else { short x=o->pos*o->charWidth,y=o->line*o->lineHeight; o->hiliteR.right=o->hiliteR.left=x;o->hiliteR.bottom=o->hiliteR.top=y; o->hiliteR.right+=o->charWidth;o->hiliteR.bottom+=o->lineHeight; y+=o->lineHeight; EraseRect(&o->hiliteR); MoveTo(x,y-3); DrawChar(c); if (o->attrib&bold) { MoveTo(x+1,y-3); DrawChar(c); } if (o->attrib&underline) { MoveTo(x,y-3); LineTo(x+o->charWidth,y-3); } if (o->attrib&outline) InvertRect(&o->hiliteR); ++o->pos; if (o->wraparound) if (o->pos>=o->maxPos) { o->line++; o->pos=0; termLF(o); } } checkPos(o,&o->pos,&o->line); o->hiliteR=getPos(o,o->pos,o->line); }