/**
* ANSI.java
*
* Created on August 3, 2007, 8:37 PM
*
* @author pquiring
*/
import java.awt.event.KeyEvent;
import java.awt.Color;
import javaforce.*;
public class ANSI {
public ANSI(Buffer buffer, boolean telnet) {
orgForeColor = buffer.getForeColor();
orgBackColor = buffer.getBackColor();
}
public final static char ESC = 0x1b;
public final static int clrs[][] = {
//Black ,Red ,Green ,Yellow ,Blue ,Magenta ,Cyan ,White
{0x000000,0x880000,0x008800,0x888800,0x000088,0x880088,0x008888,0x888888}, //low
{0x444444,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00ffff,0xffffff} //high
};
public boolean altfnt = false; //use alternate font
public boolean keypadMode = false;
public char altcode = '['; //prefix some key codes with this
public int high = 0; //high intensity color (0/1)
private int savedx = -1, savedy = -1;
private Color orgForeColor, orgBackColor;
private boolean telnet;
public int numc;
public int nums[] = new int[16];
public char encodeChar(char ch) {
if (!altfnt) {
if (ch > 127 && ch < 256) ch = ASCII8.convert(ch);
return ch;
} else {
char ret = altfntchars[ch];
if (ret == 0) ret = ch;
if (ret > 127 && ret < 256) ret = ASCII8.convert(ret);
return ret;
}
}
public char[] encodeString(char buf[], int buflen) {
char ret[] = new char[buflen];
for(int a=0;a<buflen;a++) ret[a] = encodeChar(buf[a]);
return ret;
}
public void keyPressed(int keyCode, int keyMods, Buffer buffer) {
//an easy way to find codes is to run "xterm sh" and press keys - "sh" doesn't understand ANSI and echo'es them back
String str = null;
if (keyMods == KeyEvent.CTRL_MASK) {
if ((keyCode >= KeyEvent.VK_A) && (keyCode <= KeyEvent.VK_Z)) {
str = "" + (char)(keyCode - KeyEvent.VK_A + 1);
}
switch (keyCode) {
case KeyEvent.VK_UP: str = "" + ESC + "" + altcode + "1;5A"; break;
case KeyEvent.VK_DOWN: str = "" + ESC + "" + altcode + "1;5B"; break;
case KeyEvent.VK_RIGHT:str = "" + ESC + "" + altcode + "1;5C"; break;
case KeyEvent.VK_LEFT: str = "" + ESC + "" + altcode + "1;5D"; break;
}
}
if (keyMods == KeyEvent.ALT_MASK) {
switch (keyCode) {
case KeyEvent.VK_UP: str = "" + ESC + "" + altcode + "1;3A"; break;
case KeyEvent.VK_DOWN: str = "" + ESC + "" + altcode + "1;3B"; break;
case KeyEvent.VK_RIGHT:str = "" + ESC + "" + altcode + "1;3C"; break;
case KeyEvent.VK_LEFT: str = "" + ESC + "" + altcode + "1;3D"; break;
}
}
if (keyMods == KeyEvent.SHIFT_MASK) {
switch (keyCode) {
case KeyEvent.VK_UP: str = "" + ESC + "" + altcode + "1;2A"; break;
case KeyEvent.VK_DOWN: str = "" + ESC + "" + altcode + "1;2B"; break;
case KeyEvent.VK_RIGHT:str = "" + ESC + "" + altcode + "1;2C"; break;
case KeyEvent.VK_LEFT: str = "" + ESC + "" + altcode + "1;2D"; break;
case KeyEvent.VK_F1: str = "" + ESC + "[25~"; break; //KEY_F13
case KeyEvent.VK_F2: str = "" + ESC + "[26~"; break; //KEY_F14
case KeyEvent.VK_F3: str = "" + ESC + "[28~"; break; //KEY_F15
case KeyEvent.VK_F4: str = "" + ESC + "[29~"; break; //KEY_F16
case KeyEvent.VK_F5: str = "" + ESC + "[31~"; break; //KEY_F17
case KeyEvent.VK_F6: str = "" + ESC + "[32~"; break; //KEY_F18
case KeyEvent.VK_F7: str = "" + ESC + "[33~"; break; //KEY_F19
case KeyEvent.VK_F8: str = "" + ESC + "[34~"; break; //KEY_F20
}
}
if (str != null) buffer.output(str.toCharArray());
if (keyMods != 0) return;
switch (keyCode) {
case KeyEvent.VK_UP: str = "" + ESC + "" + altcode + "A"; break;
case KeyEvent.VK_DOWN: str = "" + ESC + "" + altcode + "B"; break;
case KeyEvent.VK_RIGHT:str = "" + ESC + "" + altcode + "C"; break;
case KeyEvent.VK_LEFT: str = "" + ESC + "" + altcode + "D"; break;
case KeyEvent.VK_HOME: str = "" + ESC + "[H"; break;
case KeyEvent.VK_END: str = "" + ESC + "[F"; break;
case KeyEvent.VK_F1: str = "" + ESC + altcode + "P"; break;
case KeyEvent.VK_F2: str = "" + ESC + altcode + "Q"; break;
case KeyEvent.VK_F3: str = "" + ESC + altcode + "R"; break;
case KeyEvent.VK_F4: str = "" + ESC + altcode + "S"; break;
case KeyEvent.VK_F5:
if (telnet)
str = "" + Telnet.IAC + Telnet.BRK; //BREAK
else
str = "" + ESC + "[15~";
break;
case KeyEvent.VK_F6: str = "" + ESC + "[17~"; break;
case KeyEvent.VK_F7: str = "" + ESC + "[18~"; break;
case KeyEvent.VK_F8: str = "" + ESC + "[19~"; break;
case KeyEvent.VK_F9: str = "" + ESC + "[20~"; break;
case KeyEvent.VK_F10: str = "" + ESC + "[21~"; break;
case KeyEvent.VK_F11: str = "" + ESC + "[23~"; break;
case KeyEvent.VK_F12: str = "" + ESC + "[24~"; break;
case KeyEvent.VK_DELETE: str = "" + ESC + "[3~"; break;
case KeyEvent.VK_PAGE_UP: str = "" + ESC + "[5~"; break; //PREV
case KeyEvent.VK_PAGE_DOWN: str = "" + ESC + "[6~"; break; //NEXT
case KeyEvent.VK_PAUSE: str = "" + Telnet.IAC + Telnet.BRK; break; //BREAK
}
if (str != null) buffer.output(str.toCharArray());
}
public boolean decode(char code[], int codelen, Buffer buffer) {
int x,y;
x = buffer.getx();
y = buffer.gety();
if (codelen < 2) return false;
switch (code[1]) {
case 'H':
//add tab stop at cursor position
JFLog.log("ANSI:Not implemented:" + code[1]);
return true;
case 'M': //move cursor up one (scroll down if needed)
if (y <= buffer.gety1())
buffer.scrollDown(1);
else
buffer.gotoPos(x, y - 1);
return true;
case 'D': //move cursor down one (scroll up if needed)
if (y >= buffer.gety2())
buffer.scrollUp(1);
else
buffer.gotoPos(x, y + 1);
return true;
case '7':
//save cursor pos
savedx = x;
savedy = y;
return true;
case '8':
//restore cursor pos
if (savedx == -1) return true;
buffer.gotoPos(savedx, savedy);
return true;
case '=':
keypadMode = true;
return true;
case '>':
keypadMode = false;
return true;
case '#':
case '(':
case '[':
case ']': break;
default:
JFLog.log("ANSI:Unknown code:" + code[1]);
return true; //ignore unknown code
}
if (codelen < 3) return false;
switch (code[1]) {
case '#':
/*
switch (code[2]) {
case '3': //large font?
case '4': //small font?
}
*/
break;
case '(':
switch (code[2]) {
case 'A': altfnt = false; break; //UK char set
case 'B': altfnt = false; break; //US char set
case '0': altfnt = true; break; //line drawing char set
case '1': altfnt = false; break; //alt ROM char set
case '2': altfnt = false; break; //alt ROM special char set
}
break;
case ']': //operating system command
if (code[codelen-1] != 7) return false; //incomplete
//OSC is currently not supported
break;
case '[': //control sequence introducer
if (!(
((code[codelen-1]) >= 'A' && (code[codelen-1] <= 'Z')) ||
((code[codelen-1]) >= 'a' && (code[codelen-1] <= 'z')) ||
((code[codelen-1]) == '~') ||
((code[codelen-1]) == '@'))) return false; //incomplete
if (code[2] == '?') {
decodeNums(code, codelen, 3);
switch (code[codelen-1]) {
case 'h': //set private functions
for(int a=0;a<numc;a++) {
switch (nums[a]) {
case 1: altcode = 'O'; break; //cursor key mode : control/app functions
case 7: buffer.setAutoWrap(false); break;
case 12: break; //TODO : start blinking cursor
case 25: break; //TODO : show cursor
default: JFLog.log("ANSI:Unknown [ ? h:" + nums[a]);
}
}
return true;
case 'l': //reset private functions
for(int a=0;a<numc;a++) {
switch (nums[a]) {
case 1: altcode = '['; break; //cursor key mode : ANSI control sequences
case 7: buffer.setAutoWrap(true); break;
case 12: break; //TODO : stop blinking cursor
case 25: break; //TODO : hide cursor
default: JFLog.log("ANSI:Unknown [ ? l:" + nums[a]);
}
}
return true;
default:
JFLog.log("ANSI:Unknown [ ? code:" + code[codelen-1]);
//ignore unknown ? code
return true;
}
}
decodeNums(code, codelen, 2);
switch (code[codelen-1]) {
case 'J':
if (numc == 0) nums[0] = 0;
switch (nums[0]) {
case 0:
//from cursor to end of screen
for(int a=x;a<=buffer.sx;a++) buffer.setChar(a, y, ' '); //erase partial line
for(int b=y+1;b<=buffer.sy;b++) for(int a=1;a<=buffer.sx;a++) buffer.setChar(a, b, ' ');
break;
case 1:
//from begining to cursor
for(int a=1;a<x;a++) buffer.setChar(a, y, ' '); //erase partial line
for(int b=1;b<y;b++) for(int a=1;a<=buffer.sx;a++) buffer.setChar(a, b, ' ');
break;
case 2:
//entire screen (reposition cursor to (1,1) too)
buffer.clrscr();
break;
}
break;
case 'K':
if (numc == 0) nums[0] = 0;
switch (nums[0]) {
case 0:
//erase from cursor to end of line
for(int a=x;a<=buffer.sx;a++) buffer.setChar(a, y, ' ');
break;
case 1:
//from beginning of line to cursor
for(int a=1;a<x;a++) buffer.setChar(a, y, ' ');
break;
case 2:
//whole line
for(int a=1;a<=buffer.sx;a++) buffer.setChar(a, y, ' ');
break;
}
break;
case 'H':
case 'f':
if (numc == 2 && nums[1] == 0) numc = 1;
if (numc == 1 && nums[0] == 0) numc = 0;
switch (numc) {
case 2:
buffer.gotoPos(min(buffer.sx,nums[1]),min(buffer.sy,nums[0]));
break;
case 1:
buffer.gotoPos(1, min(buffer.sy,nums[0]));
break;
case 0:
buffer.gotoPos(1,1);
break;
}
break;
case 'A': //up
if (numc == 0) nums[0] = 1; if (y-nums[0]>1 ) buffer.gotoPos(x,y-nums[0]); else buffer.gotoPos(x,1);
break;
case 'B': //down
if (numc == 0) nums[0] = 1; if (y+nums[0]<buffer.sy) buffer.gotoPos(x,y+nums[0]); else buffer.gotoPos(x,buffer.sy);
break;
case 'C': //forward
if (numc == 0) nums[0] = 1; if (x+nums[0]<buffer.sx) buffer.gotoPos(x+nums[0],y); else buffer.gotoPos(buffer.sx,y);
break;
case 'D': //backwards
if (numc == 0) nums[0] = 1; if (x-nums[0]>1 ) buffer.gotoPos(x-nums[0],y); else buffer.gotoPos(1,y);
break;
case 'L': //insert lines (only if within margin area)
if ((y < buffer.gety1()) || (y > buffer.gety2())) break;
if (numc == 0) nums[0] = 1;
int oy1 = buffer.gety1();
buffer.sety1(y);
buffer.scrollDown(nums[0]);
buffer.sety1(oy1);
break;
case 'r':
//define rows that scroll (margin)
if (numc != 2) break;
buffer.sety1(nums[0]);
buffer.sety2(nums[1]);
break;
case 'm':
//colour
if (numc == 0) {numc = 1; nums[0] = 0;}
for(int a=0;a<numc;a++) {
if (nums[a] == 1) {high = 1; continue;} //bold
if (nums[a] == 2) {high = 0; continue;}
}
for(int a=0;a<numc;a++) {
if (nums[a] == 0) {
//normal
high = 0;
buffer.setBlinker(false);
buffer.setReverse(false);
buffer.setForeColor(orgForeColor);
buffer.setBackColor(orgBackColor);
continue;
}
// if (nums[a] == 4) {continue;} //underline (not implemented)
if (nums[a] == 5) {buffer.setBlinker(true); continue;}
if (nums[a] == 7) {buffer.setReverse(true); continue;} //reverse
// if (nums[a] == 8) {continue;} //invisible (not implemented) [vt300]
// if (nums[a] == 24) {continue;} //not underline (not implemented)
if (nums[a] == 25) {buffer.setBlinker(false); continue;}
if (nums[a] == 27) {buffer.setReverse(false); continue;} //Positive (not inverse)
// if (nums[a] == 28) {continue;} //visible (not implemented) [vt300]
if ((nums[a] >= 30) && (nums[a] <= 37)) {buffer.setForeColor(clrs[high][nums[a]-30]); continue;}
if (nums[a] == 39) {buffer.setForeColor(orgForeColor); continue;} //default (org)
if ((nums[a] >= 40) && (nums[a] <= 47)) {buffer.setBackColor(clrs[0][nums[a]-40]); continue;}
if (nums[a] == 49) {buffer.setBackColor(orgBackColor); continue;} //default (org)
}
break;
case 'n':
//query cursor position "ESC[row;colR"
if ((numc != 1) || (nums[0] != 6)) break;
String str = "" + ESC + '[' + buffer.gety() + ';' + buffer.getx() + 'R';
buffer.output(str.toCharArray());
break;
case 'P':
//delete nums[0] chars at cursor
if (numc == 0) {numc = 1; nums[0] = 1;}
for(int cnt=0;cnt<nums[0];cnt++) buffer.delete();
break;
case '@':
//insert nums[0] chars at cursor
if (numc == 0) {numc = 1; nums[0] = 1;}
for(int cnt=0;cnt<nums[0];cnt++) buffer.insert();
break;
case 's':
//save cursor pos
savedx = buffer.getx();
savedy = buffer.gety();
break;
case 'u':
//restore cursor pos
if (savedx == -1) return true;
buffer.gotoPos(savedx, savedy);
break;
case 'g': //not implemented
//0 = clear tab stop at cursor location
//3 = clear all tab stops
break;
case 'd': //line position absolute (default = 1)
if (numc == 0) nums[0] = 1;
buffer.gotoPos(x, nums[0]);
break;
case 'e': //line position relative (default = row+1)
if (numc == 0) nums[0] = 1;
buffer.gotoPos(x, y + nums[0]);
break;
case 'G': //column position absolute (default = 1)
if (numc == 0) nums[0] = 1;
buffer.gotoPos(nums[0], y);
break;
case 'X': //delete 'x' chars
if (numc == 0) nums[0] = 1;
int cnt = nums[0];
while (cnt > 0) {
buffer.setChar(x, y, ' ');
x++;
if (x == buffer.sx+1) {
x = 1;
y++;
if (y == buffer.sy+1) break;
}
cnt--;
}
break;
default:
//ignore unknown code
break;
}
}
return true;
}
private void decodeNums(char code[], int codelen, int start) {
//decode #s encoded into code[]
int c=start,s,e,t,m;
numc = 0;
code[codelen] = 'x';
while (c < codelen) {
while ((code[c]==';')||(code[c]==',')) {
nums[numc++] = 0;
c++;
}
if (!((code[c]>='0') && (code[c]<='9'))) break;
s=c;
while ((code[c]>='0') && (code[c]<='9')) {
c++;
}
e=c;
t=0;
m=1;
while (c!=s) {
c--;
t+=(code[c]-'0') * m;
m*=10;
}
nums[numc++]=t;
c=e;
if ((code[c] == ';') || (code[c] == ',')) c++;
}
}
private int min(int v1, int v2) {
if (v1 < v2) return v1;
return v2;
}
private int max(int v1, int v2) {
if (v1 > v2) return v1;
return v2;
}
private static char altfntchars[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //16
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //32
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //48
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //64
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //80
0,177,0,0,0,0,0,0,0,0,217,191,218,192,0,0, //96
0,196,0,0,195,180,193,194,179,0,0,0,0,0,0,0, //112
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //128
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
}