/*
* This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
*
* (c) Matthias L. Jugel, Marcus Meiner 1996-2005. All Rights Reserved.
*
* Please visit http://javatelnet.org/ for updates and contact.
*
* --LICENSE NOTICE--
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* --LICENSE NOTICE--
*
*/
package de.mud.terminal;
import java.util.Properties;
/**
* Implementation of a VT terminal emulation plus ANSI compatible.
*
* Maintainer: Marcus Meißner
*
* @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $
*/
@SuppressWarnings("unused")
public abstract class vt320 extends VDUBuffer implements VDUInput {
/**
* The current version id tag.
*
* $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $
*
*/
public final static String ID = "$Id: vt320.java 507 2005-10-25 10:14:52Z marcus $";
/** the debug level */
private final static int debug = 0;
private StringBuilder debugStr;
public abstract void debug(String notice);
/**
* Write an answer back to the remote host. This is needed to be able to send terminal answers
* requests like status and type information.
*
* @param b
* the array of bytes to be sent
*/
public abstract void write(byte[] b);
/**
* Write an answer back to the remote host. This is needed to be able to send terminal answers
* requests like status and type information.
*
* @param b
* the array of bytes to be sent
*/
public abstract void write(int b);
/**
* Play the beep sound ...
*/
public void beep() { /* do nothing by default */
}
/**
* Convenience function for putString(char[], int, int)
*/
public void putString(String s) {
int len = s.length();
char[] tmp = new char[len];
s.getChars(0, len, tmp, 0);
putString(tmp, null, 0, len);
}
/**
* Put string at current cursor position. Moves cursor according to the String. Does NOT wrap.
*
* @param s
* character array
* @param start
* place to start in array
* @param len
* number of characters to process
*/
public void putString(char[] s, byte[] fullwidths, int start, int len) {
if (len > 0) {
// markLine(R, 1);
int lastChar = -1;
char c;
boolean isWide = false;
for (int i = 0; i < len; i++) {
c = s[start + i];
// Shortcut for my favorite ASCII
if (c <= 0x7F) {
if (lastChar != -1) {
putChar((char) lastChar, isWide, false);
}
lastChar = c;
isWide = false;
} else if (!Character.isLowSurrogate(c) && !Character.isHighSurrogate(c)) {
if (Character.getType(c) == Character.NON_SPACING_MARK) {
if (lastChar != -1) {
char nc = Precomposer.precompose((char) lastChar, c);
putChar(nc, isWide, false);
lastChar = -1;
}
} else {
if (lastChar != -1) {
putChar((char) lastChar, isWide, false);
}
lastChar = c;
if (fullwidths != null) {
isWide = fullwidths[i] == 1;
}
}
}
}
if (lastChar != -1) {
putChar((char) lastChar, isWide, false);
}
setCursorPosition(C, R);
redraw();
}
}
protected void sendTelnetCommand(byte cmd) {
}
/**
* Sent the changed window size from the terminal to all listeners.
*/
protected void setWindowSize(int c, int r) {
/* To be overridden by Terminal.java */
}
@Override
public void setScreenSize(int c, int r, boolean broadcast) {
// int oldrows = height;
if (debug > 2) {
if (debugStr == null) {
debugStr = new StringBuilder();
}
debugStr.append("setscreensize (").append(c).append(',').append(r).append(',')
.append(broadcast).append(')');
debug(debugStr.toString());
debugStr.setLength(0);
}
super.setScreenSize(c, r, false);
boolean cursorChanged = false;
// Don't let the cursor go off the screen.
if (C >= c) {
C = c - 1;
cursorChanged = true;
}
if (R >= r) {
R = r - 1;
cursorChanged = true;
}
if (cursorChanged) {
setCursorPosition(C, R);
redraw();
}
if (broadcast) {
setWindowSize(c, r); /* broadcast up */
}
}
/**
* Create a new vt320 terminal and intialize it with useful settings.
*/
public vt320(int width, int height) {
super(width, height);
debugStr = new StringBuilder();
setVMS(false);
setIBMCharset(false);
setTerminalID("vt320");
setBufferSize(100);
// setBorder(2, false);
gx = new char[4];
reset();
/* top row of numpad */
PF1 = "\u001bOP";
PF2 = "\u001bOQ";
PF3 = "\u001bOR";
PF4 = "\u001bOS";
/* the 3x2 keyblock on PC keyboards */
Insert = new String[4];
Remove = new String[4];
KeyHome = new String[4];
KeyEnd = new String[4];
NextScn = new String[4];
PrevScn = new String[4];
Escape = new String[4];
BackSpace = new String[4];
TabKey = new String[4];
Insert[0] = Insert[1] = Insert[2] = Insert[3] = "\u001b[2~";
Remove[0] = Remove[1] = Remove[2] = Remove[3] = "\u001b[3~";
PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[5~";
NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[6~";
KeyHome[0] = KeyHome[1] = KeyHome[2] = KeyHome[3] = "\u001b[H";
KeyEnd[0] = KeyEnd[1] = KeyEnd[2] = KeyEnd[3] = "\u001b[F";
Escape[0] = Escape[1] = Escape[2] = Escape[3] = "\u001b";
if (vms) {
BackSpace[1] = "" + (char) 10; // VMS shift deletes word back
BackSpace[2] = "\u0018"; // VMS control deletes line back
BackSpace[0] = BackSpace[3] = "\u007f"; // VMS other is delete
} else {
// BackSpace[0] = BackSpace[1] = BackSpace[2] = BackSpace[3] = "\b";
// ConnectBot modifications.
BackSpace[0] = "\b";
BackSpace[1] = "\u007f";
BackSpace[2] = "\u001b[3~";
BackSpace[3] = "\u001b[2~";
}
/* some more VT100 keys */
Find = "\u001b[1~";
Select = "\u001b[4~";
Help = "\u001b[28~";
Do = "\u001b[29~";
FunctionKey = new String[21];
FunctionKey[0] = "";
FunctionKey[1] = PF1;
FunctionKey[2] = PF2;
FunctionKey[3] = PF3;
FunctionKey[4] = PF4;
/* following are defined differently for vt220 / vt132 ... */
FunctionKey[5] = "\u001b[15~";
FunctionKey[6] = "\u001b[17~";
FunctionKey[7] = "\u001b[18~";
FunctionKey[8] = "\u001b[19~";
FunctionKey[9] = "\u001b[20~";
FunctionKey[10] = "\u001b[21~";
FunctionKey[11] = "\u001b[23~";
FunctionKey[12] = "\u001b[24~";
FunctionKey[13] = "\u001b[25~";
FunctionKey[14] = "\u001b[26~";
FunctionKey[15] = Help;
FunctionKey[16] = Do;
FunctionKey[17] = "\u001b[31~";
FunctionKey[18] = "\u001b[32~";
FunctionKey[19] = "\u001b[33~";
FunctionKey[20] = "\u001b[34~";
FunctionKeyShift = new String[21];
FunctionKeyAlt = new String[21];
FunctionKeyCtrl = new String[21];
for (int i = 0; i < 20; i++) {
FunctionKeyShift[i] = "";
FunctionKeyAlt[i] = "";
FunctionKeyCtrl[i] = "";
}
FunctionKeyShift[15] = Find;
FunctionKeyShift[16] = Select;
TabKey[0] = "\u0009";
TabKey[1] = "\u001bOP\u0009";
TabKey[2] = TabKey[3] = "";
KeyUp = new String[4];
KeyUp[0] = "\u001b[A";
KeyDown = new String[4];
KeyDown[0] = "\u001b[B";
KeyRight = new String[4];
KeyRight[0] = "\u001b[C";
KeyLeft = new String[4];
KeyLeft[0] = "\u001b[D";
Numpad = new String[10];
Numpad[0] = "\u001bOp";
Numpad[1] = "\u001bOq";
Numpad[2] = "\u001bOr";
Numpad[3] = "\u001bOs";
Numpad[4] = "\u001bOt";
Numpad[5] = "\u001bOu";
Numpad[6] = "\u001bOv";
Numpad[7] = "\u001bOw";
Numpad[8] = "\u001bOx";
Numpad[9] = "\u001bOy";
KPMinus = PF4;
KPComma = "\u001bOl";
KPPeriod = "\u001bOn";
KPEnter = "\u001bOM";
NUMPlus = new String[4];
NUMPlus[0] = "+";
NUMDot = new String[4];
NUMDot[0] = ".";
}
public void setBackspace(int type) {
switch (type) {
case DELETE_IS_DEL:
BackSpace[0] = "\u007f";
BackSpace[1] = "\b";
break;
case DELETE_IS_BACKSPACE:
BackSpace[0] = "\b";
BackSpace[1] = "\u007f";
break;
}
}
/**
* Create a default vt320 terminal with 80 columns and 24 lines.
*/
public vt320() {
this(80, 24);
}
/**
* Terminal is mouse-aware and requires (x,y) coordinates of on the terminal (character
* coordinates) and the button clicked.
*
* @param x
* @param y
* @param modifiers
*/
public void mousePressed(int x, int y, int modifiers) {
if (mouserpt == 0) {
return;
}
int mods = modifiers;
mousebut = 3;
if ((mods & 16) == 16) {
mousebut = 0;
}
if ((mods & 8) == 8) {
mousebut = 1;
}
if ((mods & 4) == 4) {
mousebut = 2;
}
int mousecode;
if (mouserpt == 9) {
mousecode = 0x20 | mousebut;
} else {
mousecode = mousebut | 0x20 | ((mods & 7) << 2);
}
byte b[] = new byte[6];
b[0] = 27;
b[1] = (byte) '[';
b[2] = (byte) 'M';
b[3] = (byte) mousecode;
b[4] = (byte) (0x20 + x + 1);
b[5] = (byte) (0x20 + y + 1);
write(b); // FIXME: writeSpecial here
}
/**
* Terminal is mouse-aware and requires the coordinates and button of the release.
*
* @param x
* @param y
* @param modifiers
*/
public void mouseReleased(int x, int y, int modifiers) {
if (mouserpt == 0) {
return;
}
/*
* problem is tht modifiers still have the released button set in them. int mods = modifiers;
* mousebut = 3; if ((mods & 16)==16) mousebut=0; if ((mods & 8)==8 ) mousebut=1; if ((mods &
* 4)==4 ) mousebut=2;
*/
int mousecode;
if (mouserpt == 9) {
mousecode = 0x20 + mousebut; /* same as press? appears so. */
} else {
mousecode = '#';
}
byte b[] = new byte[6];
b[0] = 27;
b[1] = (byte) '[';
b[2] = (byte) 'M';
b[3] = (byte) mousecode;
b[4] = (byte) (0x20 + x + 1);
b[5] = (byte) (0x20 + y + 1);
write(b); // FIXME: writeSpecial here
mousebut = 0;
}
/** we should do localecho (passed from other modules). false is default */
private boolean localecho = false;
/**
* Enable or disable the local echo property of the terminal.
*
* @param echo
* true if the terminal should echo locally
*/
public void setLocalEcho(boolean echo) {
localecho = echo;
}
/**
* Enable the VMS mode of the terminal to handle some things differently for VMS hosts.
*
* @param vms
* true for vms mode, false for normal mode
*/
public void setVMS(boolean vms) {
this.vms = vms;
}
/**
* Enable the usage of the IBM character set used by some BBS's. Special graphical character are
* available in this mode.
*
* @param ibm
* true to use the ibm character set
*/
public void setIBMCharset(boolean ibm) {
useibmcharset = ibm;
}
/**
* Override the standard key codes used by the terminal emulation.
*
* @param codes
* a properties object containing key code definitions
*/
public void setKeyCodes(Properties codes) {
String res, prefixes[] = { "", "S", "C", "A" };
int i;
for (i = 0; i < 10; i++) {
res = codes.getProperty("NUMPAD" + i);
if (res != null) {
Numpad[i] = unEscape(res);
}
}
for (i = 1; i < 20; i++) {
res = codes.getProperty("F" + i);
if (res != null) {
FunctionKey[i] = unEscape(res);
}
res = codes.getProperty("SF" + i);
if (res != null) {
FunctionKeyShift[i] = unEscape(res);
}
res = codes.getProperty("CF" + i);
if (res != null) {
FunctionKeyCtrl[i] = unEscape(res);
}
res = codes.getProperty("AF" + i);
if (res != null) {
FunctionKeyAlt[i] = unEscape(res);
}
}
for (i = 0; i < 4; i++) {
res = codes.getProperty(prefixes[i] + "PGUP");
if (res != null) {
PrevScn[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "PGDOWN");
if (res != null) {
NextScn[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "END");
if (res != null) {
KeyEnd[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "HOME");
if (res != null) {
KeyHome[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "INSERT");
if (res != null) {
Insert[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "REMOVE");
if (res != null) {
Remove[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "UP");
if (res != null) {
KeyUp[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "DOWN");
if (res != null) {
KeyDown[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "LEFT");
if (res != null) {
KeyLeft[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "RIGHT");
if (res != null) {
KeyRight[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "ESCAPE");
if (res != null) {
Escape[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "BACKSPACE");
if (res != null) {
BackSpace[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "TAB");
if (res != null) {
TabKey[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "NUMPLUS");
if (res != null) {
NUMPlus[i] = unEscape(res);
}
res = codes.getProperty(prefixes[i] + "NUMDECIMAL");
if (res != null) {
NUMDot[i] = unEscape(res);
}
}
}
/**
* Set the terminal id used to identify this terminal.
*
* @param terminalID
* the id string
*/
public void setTerminalID(String terminalID) {
this.terminalID = terminalID;
if (terminalID.equals("scoansi")) {
FunctionKey[1] = "\u001b[M";
FunctionKey[2] = "\u001b[N";
FunctionKey[3] = "\u001b[O";
FunctionKey[4] = "\u001b[P";
FunctionKey[5] = "\u001b[Q";
FunctionKey[6] = "\u001b[R";
FunctionKey[7] = "\u001b[S";
FunctionKey[8] = "\u001b[T";
FunctionKey[9] = "\u001b[U";
FunctionKey[10] = "\u001b[V";
FunctionKey[11] = "\u001b[W";
FunctionKey[12] = "\u001b[X";
FunctionKey[13] = "\u001b[Y";
FunctionKey[14] = "?";
FunctionKey[15] = "\u001b[a";
FunctionKey[16] = "\u001b[b";
FunctionKey[17] = "\u001b[c";
FunctionKey[18] = "\u001b[d";
FunctionKey[19] = "\u001b[e";
FunctionKey[20] = "\u001b[f";
PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[I";
NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[G";
// more theoretically.
}
}
public void setAnswerBack(String ab) {
answerBack = unEscape(ab);
}
/**
* Get the terminal id used to identify this terminal.
*/
public String getTerminalID() {
return terminalID;
}
/**
* A small conveniance method thar converts the string to a byte array for sending.
*
* @param s
* the string to be sent
*/
private boolean write(String s, boolean doecho) {
if (debug > 2) {
debugStr.append("write(|").append(s).append("|,").append(doecho);
debug(debugStr.toString());
debugStr.setLength(0);
}
if (s == null) {
return true;
/*
* NOTE: getBytes() honours some locale, it *CONVERTS* the string. However, we output only
* 7bit stuff towards the target, and *some* 8 bit control codes. We must not mess up the
* latter, so we do hand by hand copy.
*/
}
byte arr[] = new byte[s.length()];
for (int i = 0; i < s.length(); i++) {
arr[i] = (byte) s.charAt(i);
}
write(arr);
if (doecho) {
putString(s);
}
return true;
}
private boolean write(int s, boolean doecho) {
if (debug > 2) {
debugStr.append("write(|").append(s).append("|,").append(doecho);
debug(debugStr.toString());
debugStr.setLength(0);
}
write(s);
// TODO check if character is wide
if (doecho) {
putChar((char) s, false, false);
}
return true;
}
private boolean write(String s) {
return write(s, localecho);
}
// ===================================================================
// the actual terminal emulation code comes here:
// ===================================================================
private String terminalID = "vt320";
private String answerBack = "Use Terminal.answerback to set ...\n";
// X - COLUMNS, Y - ROWS
int R, C;
int attributes = 0;
int Sc, Sr, Sa, Stm, Sbm;
char Sgr, Sgl;
char Sgx[];
int insertmode = 0;
int statusmode = 0;
boolean vt52mode = false;
boolean keypadmode = false; /* false - numeric, true - application */
boolean output8bit = false;
int normalcursor = 0;
boolean moveoutsidemargins = true;
boolean wraparound = true;
boolean sendcrlf = true;
boolean capslock = false;
boolean numlock = false;
int mouserpt = 0;
byte mousebut = 0;
boolean useibmcharset = false;
int lastwaslf = 0;
boolean usedcharsets = false;
private final static char ESC = 27;
private final static char IND = 132;
private final static char NEL = 133;
private final static char RI = 141;
private final static char SS2 = 142;
private final static char SS3 = 143;
private final static char DCS = 144;
private final static char HTS = 136;
private final static char CSI = 155;
private final static char OSC = 157;
private final static int TSTATE_DATA = 0;
private final static int TSTATE_ESC = 1; /* ESC */
private final static int TSTATE_CSI = 2; /* ESC [ */
private final static int TSTATE_DCS = 3; /* ESC P */
private final static int TSTATE_DCEQ = 4; /* ESC [? */
private final static int TSTATE_ESCSQUARE = 5; /* ESC # */
private final static int TSTATE_OSC = 6; /* ESC ] */
private final static int TSTATE_SETG0 = 7; /* ESC (? */
private final static int TSTATE_SETG1 = 8; /* ESC )? */
private final static int TSTATE_SETG2 = 9; /* ESC *? */
private final static int TSTATE_SETG3 = 10; /* ESC +? */
private final static int TSTATE_CSI_DOLLAR = 11; /* ESC [ Pn $ */
private final static int TSTATE_CSI_EX = 12; /* ESC [ ! */
private final static int TSTATE_ESCSPACE = 13; /* ESC */
private final static int TSTATE_VT52X = 14;
private final static int TSTATE_VT52Y = 15;
private final static int TSTATE_CSI_TICKS = 16;
private final static int TSTATE_CSI_EQUAL = 17; /* ESC [ = */
private final static int TSTATE_TITLE = 18; /* xterm title */
/* Keys we support */
public final static int KEY_PAUSE = 1;
public final static int KEY_F1 = 2;
public final static int KEY_F2 = 3;
public final static int KEY_F3 = 4;
public final static int KEY_F4 = 5;
public final static int KEY_F5 = 6;
public final static int KEY_F6 = 7;
public final static int KEY_F7 = 8;
public final static int KEY_F8 = 9;
public final static int KEY_F9 = 10;
public final static int KEY_F10 = 11;
public final static int KEY_F11 = 12;
public final static int KEY_F12 = 13;
public final static int KEY_UP = 14;
public final static int KEY_DOWN = 15;
public final static int KEY_LEFT = 16;
public final static int KEY_RIGHT = 17;
public final static int KEY_PAGE_DOWN = 18;
public final static int KEY_PAGE_UP = 19;
public final static int KEY_INSERT = 20;
public final static int KEY_DELETE = 21;
public final static int KEY_BACK_SPACE = 22;
public final static int KEY_HOME = 23;
public final static int KEY_END = 24;
public final static int KEY_NUM_LOCK = 25;
public final static int KEY_CAPS_LOCK = 26;
public final static int KEY_SHIFT = 27;
public final static int KEY_CONTROL = 28;
public final static int KEY_ALT = 29;
public final static int KEY_ENTER = 30;
public final static int KEY_NUMPAD0 = 31;
public final static int KEY_NUMPAD1 = 32;
public final static int KEY_NUMPAD2 = 33;
public final static int KEY_NUMPAD3 = 34;
public final static int KEY_NUMPAD4 = 35;
public final static int KEY_NUMPAD5 = 36;
public final static int KEY_NUMPAD6 = 37;
public final static int KEY_NUMPAD7 = 38;
public final static int KEY_NUMPAD8 = 39;
public final static int KEY_NUMPAD9 = 40;
public final static int KEY_DECIMAL = 41;
public final static int KEY_ADD = 42;
public final static int KEY_ESCAPE = 43;
public final static int DELETE_IS_DEL = 0;
public final static int DELETE_IS_BACKSPACE = 1;
/*
* The graphics charsets B - default ASCII A - ISO Latin 1 0 - DEC SPECIAL < - User defined ....
*/
char gx[];
char gl; // GL (left charset)
char gr; // GR (right charset)
int onegl; // single shift override for GL.
// Map from scoansi linedrawing to DEC _and_ unicode (for the stuff which
// is not in linedrawing). Got from experimenting with scoadmin.
private final static String scoansi_acs =
"Tm7k3x4u?kZl@mYjEnB\u2566DqCtAvM\u2550:\u2551N\u2557I\u2554;\u2557H\u255a0a<\u255d";
// array to store DEC Special -> Unicode mapping
// Unicode DEC Unicode name (DEC name)
private static char DECSPECIAL[] = { '\u0040', // 5f blank
'\u2666', // 60 black diamond
'\u2592', // 61 grey square
'\u2409', // 62 Horizontal tab (ht) pict. for control
'\u240c', // 63 Form Feed (ff) pict. for control
'\u240d', // 64 Carriage Return (cr) pict. for control
'\u240a', // 65 Line Feed (lf) pict. for control
'\u00ba', // 66 Masculine ordinal indicator
'\u00b1', // 67 Plus or minus sign
'\u2424', // 68 New Line (nl) pict. for control
'\u240b', // 69 Vertical Tab (vt) pict. for control
'\u2518', // 6a Forms light up and left
'\u2510', // 6b Forms light down and left
'\u250c', // 6c Forms light down and right
'\u2514', // 6d Forms light up and right
'\u253c', // 6e Forms light vertical and horizontal
'\u2594', // 6f Upper 1/8 block (Scan 1)
'\u2580', // 70 Upper 1/2 block (Scan 3)
'\u2500', // 71 Forms light horizontal or ?em dash? (Scan 5)
'\u25ac', // 72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7)
'\u005f', // 73 \u005f underscore or \u2581 lower 1/8 (Scan 9)
'\u251c', // 74 Forms light vertical and right
'\u2524', // 75 Forms light vertical and left
'\u2534', // 76 Forms light up and horizontal
'\u252c', // 77 Forms light down and horizontal
'\u2502', // 78 vertical bar
'\u2264', // 79 less than or equal
'\u2265', // 7a greater than or equal
'\u00b6', // 7b paragraph
'\u2260', // 7c not equal
'\u00a3', // 7d Pound Sign (british)
'\u00b7' // 7e Middle Dot
};
/** Strings to send on function key pressing */
private String Numpad[];
private String FunctionKey[];
private String FunctionKeyShift[];
private String FunctionKeyCtrl[];
private String FunctionKeyAlt[];
private String TabKey[];
private String KeyUp[], KeyDown[], KeyLeft[], KeyRight[];
private String KPMinus, KPComma, KPPeriod, KPEnter;
private String PF1, PF2, PF3, PF4;
private String Help, Do, Find, Select;
private String KeyHome[], KeyEnd[], Insert[], Remove[], PrevScn[], NextScn[];
private String Escape[], BackSpace[], NUMDot[], NUMPlus[];
private String osc, dcs; /* to memorize OSC & DCS control sequence */
/** vt320 state variable (internal) */
private int term_state = TSTATE_DATA;
/** in vms mode, set by Terminal.VMS property */
private boolean vms = false;
/** Tabulators */
private byte[] Tabs;
/** The list of integers as used by CSI */
private int[] DCEvars = new int[30];
private int DCEvar;
/**
* Replace escape code characters (backslash + identifier) with their respective codes.
*
* @param tmp
* the string to be parsed
* @return a unescaped string
*/
static String unEscape(String tmp) {
int idx = 0, oldidx = 0;
String cmd;
// f.println("unescape("+tmp+")");
cmd = "";
while ((idx = tmp.indexOf('\\', oldidx)) >= 0 && ++idx <= tmp.length()) {
cmd += tmp.substring(oldidx, idx - 1);
if (idx == tmp.length()) {
return cmd;
}
switch (tmp.charAt(idx)) {
case 'b':
cmd += "\b";
break;
case 'e':
cmd += "\u001b";
break;
case 'n':
cmd += "\n";
break;
case 'r':
cmd += "\r";
break;
case 't':
cmd += "\t";
break;
case 'v':
cmd += "\u000b";
break;
case 'a':
cmd += "\u0012";
break;
default:
if ((tmp.charAt(idx) >= '0') && (tmp.charAt(idx) <= '9')) {
int i;
for (i = idx; i < tmp.length(); i++) {
if ((tmp.charAt(i) < '0') || (tmp.charAt(i) > '9')) {
break;
}
}
cmd += (char) Integer.parseInt(tmp.substring(idx, i));
idx = i - 1;
} else {
cmd += tmp.substring(idx, ++idx);
}
break;
}
oldidx = ++idx;
}
if (oldidx <= tmp.length()) {
cmd += tmp.substring(oldidx);
}
return cmd;
}
/**
* A small conveniance method thar converts a 7bit string to the 8bit version depending on
* VT52/Output8Bit mode.
*
* @param s
* the string to be sent
*/
private boolean writeSpecial(String s) {
if (s == null) {
return true;
}
if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == 'O'))) {
if (vt52mode) {
if ((s.charAt(2) >= 'P') && (s.charAt(2) <= 'S')) {
s = "\u001b" + s.substring(2); /* ESC x */
} else {
s = "\u001b?" + s.substring(2); /* ESC ? x */
}
} else {
if (output8bit) {
s = "\u008f" + s.substring(2); /* SS3 x */
} /* else keep string as it is */
}
}
if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == '['))) {
if (output8bit) {
s = "\u009b" + s.substring(2); /* CSI ... */
} /* else keep */
}
return write(s, false);
}
/**
* main keytyping event handler...
*/
public void keyPressed(int keyCode, char keyChar, int modifiers) {
boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0;
boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0;
boolean alt = (modifiers & VDUInput.KEY_ALT) != 0;
if (debug > 1) {
debugStr.append("keyPressed(").append(keyCode).append(", ").append((int) keyChar)
.append(", ").append(modifiers).append(')');
debug(debugStr.toString());
debugStr.setLength(0);
}
int xind;
String fmap[];
xind = 0;
fmap = FunctionKey;
if (shift) {
fmap = FunctionKeyShift;
xind = 1;
}
if (control) {
fmap = FunctionKeyCtrl;
xind = 2;
}
if (alt) {
fmap = FunctionKeyAlt;
xind = 3;
}
switch (keyCode) {
case KEY_PAUSE:
if (shift || control) {
sendTelnetCommand((byte) 243); // BREAK
}
break;
case KEY_F1:
writeSpecial(fmap[1]);
break;
case KEY_F2:
writeSpecial(fmap[2]);
break;
case KEY_F3:
writeSpecial(fmap[3]);
break;
case KEY_F4:
writeSpecial(fmap[4]);
break;
case KEY_F5:
writeSpecial(fmap[5]);
break;
case KEY_F6:
writeSpecial(fmap[6]);
break;
case KEY_F7:
writeSpecial(fmap[7]);
break;
case KEY_F8:
writeSpecial(fmap[8]);
break;
case KEY_F9:
writeSpecial(fmap[9]);
break;
case KEY_F10:
writeSpecial(fmap[10]);
break;
case KEY_F11:
writeSpecial(fmap[11]);
break;
case KEY_F12:
writeSpecial(fmap[12]);
break;
case KEY_UP:
writeSpecial(KeyUp[xind]);
break;
case KEY_DOWN:
writeSpecial(KeyDown[xind]);
break;
case KEY_LEFT:
writeSpecial(KeyLeft[xind]);
break;
case KEY_RIGHT:
writeSpecial(KeyRight[xind]);
break;
case KEY_PAGE_DOWN:
writeSpecial(NextScn[xind]);
break;
case KEY_PAGE_UP:
writeSpecial(PrevScn[xind]);
break;
case KEY_INSERT:
writeSpecial(Insert[xind]);
break;
case KEY_DELETE:
writeSpecial(Remove[xind]);
break;
case KEY_BACK_SPACE:
writeSpecial(BackSpace[xind]);
if (localecho) {
if (BackSpace[xind] == "\b") {
putString("\b \b"); // make the last char 'deleted'
} else {
putString(BackSpace[xind]); // echo it
}
}
break;
case KEY_HOME:
writeSpecial(KeyHome[xind]);
break;
case KEY_END:
writeSpecial(KeyEnd[xind]);
break;
case KEY_NUM_LOCK:
if (vms && control) {
writeSpecial(PF1);
}
if (!control) {
numlock = !numlock;
}
break;
case KEY_CAPS_LOCK:
capslock = !capslock;
return;
case KEY_SHIFT:
case KEY_CONTROL:
case KEY_ALT:
return;
default:
break;
}
}
/*
* public void keyReleased(KeyEvent evt) { if (debug > 1) debug("keyReleased("+evt+")"); // ignore
* }
*/
/**
* Handle key Typed events for the terminal, this will get all normal key types, but no
* shift/alt/control/numlock.
*/
public void keyTyped(int keyCode, char keyChar, int modifiers) {
boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0;
boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0;
boolean alt = (modifiers & VDUInput.KEY_ALT) != 0;
if (debug > 1) {
debug("keyTyped(" + keyCode + ", " + (int) keyChar + ", " + modifiers + ")");
}
if (keyChar == '\t') {
if (shift) {
write(TabKey[1], false);
} else {
if (control) {
write(TabKey[2], false);
} else {
if (alt) {
write(TabKey[3], false);
} else {
write(TabKey[0], false);
}
}
}
return;
}
if (alt) {
write(((char) (keyChar | 0x80)));
return;
}
if (((keyCode == KEY_ENTER) || (keyChar == 10)) && !control) {
write('\r');
if (localecho) {
putString("\r\n"); // bad hack
}
return;
}
if ((keyCode == 10) && !control) {
debug("Sending \\r");
write('\r');
return;
}
// FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @,
// so we can't just use it here... will probably break some other VMS
// codes. -Marcus
// if(((!vms && keyChar == '2') || keyChar == '@' || keyChar == ' ')
// && control)
if (((!vms && keyChar == '2') || keyChar == ' ') && control) {
write(0);
}
if (vms) {
if (keyChar == 127 && !control) {
if (shift) {
writeSpecial(Insert[0]); // VMS shift delete = insert
} else {
writeSpecial(Remove[0]); // VMS delete = remove
}
return;
} else if (control) {
switch (keyChar) {
case '0':
writeSpecial(Numpad[0]);
return;
case '1':
writeSpecial(Numpad[1]);
return;
case '2':
writeSpecial(Numpad[2]);
return;
case '3':
writeSpecial(Numpad[3]);
return;
case '4':
writeSpecial(Numpad[4]);
return;
case '5':
writeSpecial(Numpad[5]);
return;
case '6':
writeSpecial(Numpad[6]);
return;
case '7':
writeSpecial(Numpad[7]);
return;
case '8':
writeSpecial(Numpad[8]);
return;
case '9':
writeSpecial(Numpad[9]);
return;
case '.':
writeSpecial(KPPeriod);
return;
case '-':
case 31:
writeSpecial(KPMinus);
return;
case '+':
writeSpecial(KPComma);
return;
case 10:
writeSpecial(KPEnter);
return;
case '/':
writeSpecial(PF2);
return;
case '*':
writeSpecial(PF3);
return;
/* NUMLOCK handled in keyPressed */
default:
break;
}
/*
* Now what does this do and how did it get here. -Marcus if (shift && keyChar < 32) {
* write(PF1+(char)(keyChar + 64)); return; }
*/
}
}
// FIXME: not used?
// String fmap[];
int xind;
xind = 0;
// fmap = FunctionKey;
if (shift) {
// fmap = FunctionKeyShift;
xind = 1;
}
if (control) {
// fmap = FunctionKeyCtrl;
xind = 2;
}
if (alt) {
// fmap = FunctionKeyAlt;
xind = 3;
}
if (keyCode == KEY_ESCAPE) {
writeSpecial(Escape[xind]);
return;
}
if ((modifiers & VDUInput.KEY_ACTION) != 0) {
switch (keyCode) {
case KEY_NUMPAD0:
writeSpecial(Numpad[0]);
return;
case KEY_NUMPAD1:
writeSpecial(Numpad[1]);
return;
case KEY_NUMPAD2:
writeSpecial(Numpad[2]);
return;
case KEY_NUMPAD3:
writeSpecial(Numpad[3]);
return;
case KEY_NUMPAD4:
writeSpecial(Numpad[4]);
return;
case KEY_NUMPAD5:
writeSpecial(Numpad[5]);
return;
case KEY_NUMPAD6:
writeSpecial(Numpad[6]);
return;
case KEY_NUMPAD7:
writeSpecial(Numpad[7]);
return;
case KEY_NUMPAD8:
writeSpecial(Numpad[8]);
return;
case KEY_NUMPAD9:
writeSpecial(Numpad[9]);
return;
case KEY_DECIMAL:
writeSpecial(NUMDot[xind]);
return;
case KEY_ADD:
writeSpecial(NUMPlus[xind]);
return;
}
}
if (!((keyChar == 8) || (keyChar == 127) || (keyChar == '\r') || (keyChar == '\n'))) {
write(keyChar);
return;
}
}
private void handle_dcs(String dcs) {
debugStr.append("DCS: ").append(dcs);
debug(debugStr.toString());
debugStr.setLength(0);
}
private void handle_osc(String osc) {
if (osc.length() > 2 && osc.substring(0, 2).equals("4;")) {
// Define color palette
String[] colorData = osc.split(";");
try {
int colorIndex = Integer.parseInt(colorData[1]);
if ("rgb:".equals(colorData[2].substring(0, 4))) {
String[] rgb = colorData[2].substring(4).split("/");
int red = Integer.parseInt(rgb[0].substring(0, 2), 16) & 0xFF;
int green = Integer.parseInt(rgb[1].substring(0, 2), 16) & 0xFF;
int blue = Integer.parseInt(rgb[2].substring(0, 2), 16) & 0xFF;
display.setColor(colorIndex, red, green, blue);
}
} catch (Exception e) {
debugStr.append("OSC: invalid color sequence encountered: ").append(osc);
debug(debugStr.toString());
debugStr.setLength(0);
}
} else {
debug("OSC: " + osc);
}
}
private final static char unimap[] = {
// #
// # Name: cp437_DOSLatinUS to Unicode table
// # Unicode version: 1.1
// # Table version: 1.1
// # Table format: Format A
// # Date: 03/31/95
// # Authors: Michel Suignard
// # Lori Hoerth
// # General notes: none
// #
// # Format: Three tab-separated columns
// # Column #1 is the cp1255_WinHebrew code (in hex)
// # Column #2 is the Unicode (in hex as 0xXXXX)
// # Column #3 is the Unicode name (follows a comment sign, '#')
// #
// # The entries are in cp437_DOSLatinUS order
// #
0x0000, // #NULL
0x0001, // #START OF HEADING
0x0002, // #START OF TEXT
0x0003, // #END OF TEXT
0x0004, // #END OF TRANSMISSION
0x0005, // #ENQUIRY
0x0006, // #ACKNOWLEDGE
0x0007, // #BELL
0x0008, // #BACKSPACE
0x0009, // #HORIZONTAL TABULATION
0x000a, // #LINE FEED
0x000b, // #VERTICAL TABULATION
0x000c, // #FORM FEED
0x000d, // #CARRIAGE RETURN
0x000e, // #SHIFT OUT
0x000f, // #SHIFT IN
0x0010, // #DATA LINK ESCAPE
0x0011, // #DEVICE CONTROL ONE
0x0012, // #DEVICE CONTROL TWO
0x0013, // #DEVICE CONTROL THREE
0x0014, // #DEVICE CONTROL FOUR
0x0015, // #NEGATIVE ACKNOWLEDGE
0x0016, // #SYNCHRONOUS IDLE
0x0017, // #END OF TRANSMISSION BLOCK
0x0018, // #CANCEL
0x0019, // #END OF MEDIUM
0x001a, // #SUBSTITUTE
0x001b, // #ESCAPE
0x001c, // #FILE SEPARATOR
0x001d, // #GROUP SEPARATOR
0x001e, // #RECORD SEPARATOR
0x001f, // #UNIT SEPARATOR
0x0020, // #SPACE
0x0021, // #EXCLAMATION MARK
0x0022, // #QUOTATION MARK
0x0023, // #NUMBER SIGN
0x0024, // #DOLLAR SIGN
0x0025, // #PERCENT SIGN
0x0026, // #AMPERSAND
0x0027, // #APOSTROPHE
0x0028, // #LEFT PARENTHESIS
0x0029, // #RIGHT PARENTHESIS
0x002a, // #ASTERISK
0x002b, // #PLUS SIGN
0x002c, // #COMMA
0x002d, // #HYPHEN-MINUS
0x002e, // #FULL STOP
0x002f, // #SOLIDUS
0x0030, // #DIGIT ZERO
0x0031, // #DIGIT ONE
0x0032, // #DIGIT TWO
0x0033, // #DIGIT THREE
0x0034, // #DIGIT FOUR
0x0035, // #DIGIT FIVE
0x0036, // #DIGIT SIX
0x0037, // #DIGIT SEVEN
0x0038, // #DIGIT EIGHT
0x0039, // #DIGIT NINE
0x003a, // #COLON
0x003b, // #SEMICOLON
0x003c, // #LESS-THAN SIGN
0x003d, // #EQUALS SIGN
0x003e, // #GREATER-THAN SIGN
0x003f, // #QUESTION MARK
0x0040, // #COMMERCIAL AT
0x0041, // #LATIN CAPITAL LETTER A
0x0042, // #LATIN CAPITAL LETTER B
0x0043, // #LATIN CAPITAL LETTER C
0x0044, // #LATIN CAPITAL LETTER D
0x0045, // #LATIN CAPITAL LETTER E
0x0046, // #LATIN CAPITAL LETTER F
0x0047, // #LATIN CAPITAL LETTER G
0x0048, // #LATIN CAPITAL LETTER H
0x0049, // #LATIN CAPITAL LETTER I
0x004a, // #LATIN CAPITAL LETTER J
0x004b, // #LATIN CAPITAL LETTER K
0x004c, // #LATIN CAPITAL LETTER L
0x004d, // #LATIN CAPITAL LETTER M
0x004e, // #LATIN CAPITAL LETTER N
0x004f, // #LATIN CAPITAL LETTER O
0x0050, // #LATIN CAPITAL LETTER P
0x0051, // #LATIN CAPITAL LETTER Q
0x0052, // #LATIN CAPITAL LETTER R
0x0053, // #LATIN CAPITAL LETTER S
0x0054, // #LATIN CAPITAL LETTER T
0x0055, // #LATIN CAPITAL LETTER U
0x0056, // #LATIN CAPITAL LETTER V
0x0057, // #LATIN CAPITAL LETTER W
0x0058, // #LATIN CAPITAL LETTER X
0x0059, // #LATIN CAPITAL LETTER Y
0x005a, // #LATIN CAPITAL LETTER Z
0x005b, // #LEFT SQUARE BRACKET
0x005c, // #REVERSE SOLIDUS
0x005d, // #RIGHT SQUARE BRACKET
0x005e, // #CIRCUMFLEX ACCENT
0x005f, // #LOW LINE
0x0060, // #GRAVE ACCENT
0x0061, // #LATIN SMALL LETTER A
0x0062, // #LATIN SMALL LETTER B
0x0063, // #LATIN SMALL LETTER C
0x0064, // #LATIN SMALL LETTER D
0x0065, // #LATIN SMALL LETTER E
0x0066, // #LATIN SMALL LETTER F
0x0067, // #LATIN SMALL LETTER G
0x0068, // #LATIN SMALL LETTER H
0x0069, // #LATIN SMALL LETTER I
0x006a, // #LATIN SMALL LETTER J
0x006b, // #LATIN SMALL LETTER K
0x006c, // #LATIN SMALL LETTER L
0x006d, // #LATIN SMALL LETTER M
0x006e, // #LATIN SMALL LETTER N
0x006f, // #LATIN SMALL LETTER O
0x0070, // #LATIN SMALL LETTER P
0x0071, // #LATIN SMALL LETTER Q
0x0072, // #LATIN SMALL LETTER R
0x0073, // #LATIN SMALL LETTER S
0x0074, // #LATIN SMALL LETTER T
0x0075, // #LATIN SMALL LETTER U
0x0076, // #LATIN SMALL LETTER V
0x0077, // #LATIN SMALL LETTER W
0x0078, // #LATIN SMALL LETTER X
0x0079, // #LATIN SMALL LETTER Y
0x007a, // #LATIN SMALL LETTER Z
0x007b, // #LEFT CURLY BRACKET
0x007c, // #VERTICAL LINE
0x007d, // #RIGHT CURLY BRACKET
0x007e, // #TILDE
0x007f, // #DELETE
0x00c7, // #LATIN CAPITAL LETTER C WITH CEDILLA
0x00fc, // #LATIN SMALL LETTER U WITH DIAERESIS
0x00e9, // #LATIN SMALL LETTER E WITH ACUTE
0x00e2, // #LATIN SMALL LETTER A WITH CIRCUMFLEX
0x00e4, // #LATIN SMALL LETTER A WITH DIAERESIS
0x00e0, // #LATIN SMALL LETTER A WITH GRAVE
0x00e5, // #LATIN SMALL LETTER A WITH RING ABOVE
0x00e7, // #LATIN SMALL LETTER C WITH CEDILLA
0x00ea, // #LATIN SMALL LETTER E WITH CIRCUMFLEX
0x00eb, // #LATIN SMALL LETTER E WITH DIAERESIS
0x00e8, // #LATIN SMALL LETTER E WITH GRAVE
0x00ef, // #LATIN SMALL LETTER I WITH DIAERESIS
0x00ee, // #LATIN SMALL LETTER I WITH CIRCUMFLEX
0x00ec, // #LATIN SMALL LETTER I WITH GRAVE
0x00c4, // #LATIN CAPITAL LETTER A WITH DIAERESIS
0x00c5, // #LATIN CAPITAL LETTER A WITH RING ABOVE
0x00c9, // #LATIN CAPITAL LETTER E WITH ACUTE
0x00e6, // #LATIN SMALL LIGATURE AE
0x00c6, // #LATIN CAPITAL LIGATURE AE
0x00f4, // #LATIN SMALL LETTER O WITH CIRCUMFLEX
0x00f6, // #LATIN SMALL LETTER O WITH DIAERESIS
0x00f2, // #LATIN SMALL LETTER O WITH GRAVE
0x00fb, // #LATIN SMALL LETTER U WITH CIRCUMFLEX
0x00f9, // #LATIN SMALL LETTER U WITH GRAVE
0x00ff, // #LATIN SMALL LETTER Y WITH DIAERESIS
0x00d6, // #LATIN CAPITAL LETTER O WITH DIAERESIS
0x00dc, // #LATIN CAPITAL LETTER U WITH DIAERESIS
0x00a2, // #CENT SIGN
0x00a3, // #POUND SIGN
0x00a5, // #YEN SIGN
0x20a7, // #PESETA SIGN
0x0192, // #LATIN SMALL LETTER F WITH HOOK
0x00e1, // #LATIN SMALL LETTER A WITH ACUTE
0x00ed, // #LATIN SMALL LETTER I WITH ACUTE
0x00f3, // #LATIN SMALL LETTER O WITH ACUTE
0x00fa, // #LATIN SMALL LETTER U WITH ACUTE
0x00f1, // #LATIN SMALL LETTER N WITH TILDE
0x00d1, // #LATIN CAPITAL LETTER N WITH TILDE
0x00aa, // #FEMININE ORDINAL INDICATOR
0x00ba, // #MASCULINE ORDINAL INDICATOR
0x00bf, // #INVERTED QUESTION MARK
0x2310, // #REVERSED NOT SIGN
0x00ac, // #NOT SIGN
0x00bd, // #VULGAR FRACTION ONE HALF
0x00bc, // #VULGAR FRACTION ONE QUARTER
0x00a1, // #INVERTED EXCLAMATION MARK
0x00ab, // #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
0x00bb, // #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
0x2591, // #LIGHT SHADE
0x2592, // #MEDIUM SHADE
0x2593, // #DARK SHADE
0x2502, // #BOX DRAWINGS LIGHT VERTICAL
0x2524, // #BOX DRAWINGS LIGHT VERTICAL AND LEFT
0x2561, // #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
0x2562, // #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
0x2556, // #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
0x2555, // #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
0x2563, // #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
0x2551, // #BOX DRAWINGS DOUBLE VERTICAL
0x2557, // #BOX DRAWINGS DOUBLE DOWN AND LEFT
0x255d, // #BOX DRAWINGS DOUBLE UP AND LEFT
0x255c, // #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
0x255b, // #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
0x2510, // #BOX DRAWINGS LIGHT DOWN AND LEFT
0x2514, // #BOX DRAWINGS LIGHT UP AND RIGHT
0x2534, // #BOX DRAWINGS LIGHT UP AND HORIZONTAL
0x252c, // #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
0x251c, // #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
0x2500, // #BOX DRAWINGS LIGHT HORIZONTAL
0x253c, // #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
0x255e, // #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
0x255f, // #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
0x255a, // #BOX DRAWINGS DOUBLE UP AND RIGHT
0x2554, // #BOX DRAWINGS DOUBLE DOWN AND RIGHT
0x2569, // #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
0x2566, // #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
0x2560, // #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
0x2550, // #BOX DRAWINGS DOUBLE HORIZONTAL
0x256c, // #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
0x2567, // #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
0x2568, // #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
0x2564, // #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
0x2565, // #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
0x2559, // #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
0x2558, // #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
0x2552, // #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
0x2553, // #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
0x256b, // #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
0x256a, // #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
0x2518, // #BOX DRAWINGS LIGHT UP AND LEFT
0x250c, // #BOX DRAWINGS LIGHT DOWN AND RIGHT
0x2588, // #FULL BLOCK
0x2584, // #LOWER HALF BLOCK
0x258c, // #LEFT HALF BLOCK
0x2590, // #RIGHT HALF BLOCK
0x2580, // #UPPER HALF BLOCK
0x03b1, // #GREEK SMALL LETTER ALPHA
0x00df, // #LATIN SMALL LETTER SHARP S
0x0393, // #GREEK CAPITAL LETTER GAMMA
0x03c0, // #GREEK SMALL LETTER PI
0x03a3, // #GREEK CAPITAL LETTER SIGMA
0x03c3, // #GREEK SMALL LETTER SIGMA
0x00b5, // #MICRO SIGN
0x03c4, // #GREEK SMALL LETTER TAU
0x03a6, // #GREEK CAPITAL LETTER PHI
0x0398, // #GREEK CAPITAL LETTER THETA
0x03a9, // #GREEK CAPITAL LETTER OMEGA
0x03b4, // #GREEK SMALL LETTER DELTA
0x221e, // #INFINITY
0x03c6, // #GREEK SMALL LETTER PHI
0x03b5, // #GREEK SMALL LETTER EPSILON
0x2229, // #INTERSECTION
0x2261, // #IDENTICAL TO
0x00b1, // #PLUS-MINUS SIGN
0x2265, // #GREATER-THAN OR EQUAL TO
0x2264, // #LESS-THAN OR EQUAL TO
0x2320, // #TOP HALF INTEGRAL
0x2321, // #BOTTOM HALF INTEGRAL
0x00f7, // #DIVISION SIGN
0x2248, // #ALMOST EQUAL TO
0x00b0, // #DEGREE SIGN
0x2219, // #BULLET OPERATOR
0x00b7, // #MIDDLE DOT
0x221a, // #SQUARE ROOT
0x207f, // #SUPERSCRIPT LATIN SMALL LETTER N
0x00b2, // #SUPERSCRIPT TWO
0x25a0, // #BLACK SQUARE
0x00a0, // #NO-BREAK SPACE
};
public char map_cp850_unicode(char x) {
if (x >= 0x100) {
return x;
}
return unimap[x];
}
private void _SetCursor(int row, int col) {
int maxr = height - 1;
int tm = getTopMargin();
R = (row < 0) ? 0 : row;
C = (col < 0) ? 0 : (col >= width) ? width - 1 : col;
if (!moveoutsidemargins) {
R += tm;
maxr = getBottomMargin();
}
if (R > maxr) {
R = maxr;
}
}
private void putChar(char c, boolean isWide, boolean doshowcursor) {
int rows = height; // statusline
int columns = width;
// byte msg[];
// if (debug > 4) {
// debugStr.append("putChar(")
// .append(c)
// .append(" [")
// .append((int) c)
// .append("]) at R=")
// .append(R)
// .append(" , C=")
// .append(C)
// .append(", columns=")
// .append(columns)
// .append(", rows=")
// .append(rows);
// debug(debugStr.toString());
// debugStr.setLength(0);
// }
// markLine(R, 1);
// if (c > 255) {
// if (debug > 0)
// debug("char > 255:" + (int) c);
// //return;
// }
switch (term_state) {
case TSTATE_DATA:
/*
* FIXME: we shouldn't use chars with bit 8 set if ibmcharset. probably... but some BBS do
* anyway...
*/
if (!useibmcharset) {
boolean doneflag = true;
switch (c) {
case OSC:
osc = "";
term_state = TSTATE_OSC;
break;
case RI:
if (R > getTopMargin()) {
R--;
} else {
insertLine(R, 1, SCROLL_DOWN);
}
if (debug > 1) {
debug("RI");
}
break;
case IND:
if (debug > 2) {
debugStr.append("IND at ").append(R).append(", tm is ").append(getTopMargin())
.append(", bm is ").append(getBottomMargin());
debug(debugStr.toString());
debugStr.setLength(0);
}
if (R == getBottomMargin() || R == rows - 1) {
insertLine(R, 1, SCROLL_UP);
} else {
R++;
}
if (debug > 1) {
debug("IND (at " + R + " )");
}
break;
case NEL:
if (R == getBottomMargin() || R == rows - 1) {
insertLine(R, 1, SCROLL_UP);
} else {
R++;
}
C = 0;
if (debug > 1) {
debug("NEL (at " + R + " )");
}
break;
case HTS:
Tabs[C] = 1;
if (debug > 1) {
debug("HTS");
}
break;
case DCS:
dcs = "";
term_state = TSTATE_DCS;
break;
default:
doneflag = false;
break;
}
if (doneflag) {
break;
}
}
switch (c) {
case SS3:
onegl = 3;
break;
case SS2:
onegl = 2;
break;
case CSI: // should be in the 8bit section, but some BBS use this
DCEvar = 0;
DCEvars[0] = 0;
DCEvars[1] = 0;
DCEvars[2] = 0;
DCEvars[3] = 0;
term_state = TSTATE_CSI;
break;
case ESC:
term_state = TSTATE_ESC;
lastwaslf = 0;
break;
case 5: /* ENQ */
write(answerBack, false);
break;
case 12:
/* FormFeed, Home for the BBS world */
deleteArea(0, 0, columns, rows, attributes);
C = R = 0;
break;
case '\b': /* 8 */
C--;
if (C < 0) {
C = 0;
}
lastwaslf = 0;
break;
case '\t':
do {
// Don't overwrite or insert! TABS are not destructive, but movement!
C++;
} while (C < columns && (Tabs[C] == 0));
lastwaslf = 0;
break;
case '\r': // 13 CR
C = 0;
break;
case '\n': // 10 LF
if (debug > 3) {
debug("R= " + R + ", bm " + getBottomMargin() + ", tm=" + getTopMargin() + ", rows="
+ rows);
}
if (!vms) {
if (lastwaslf != 0 && lastwaslf != c) {
break;
}
lastwaslf = c;
/* C = 0; */
}
if (R == getBottomMargin() || R >= rows - 1) {
insertLine(R, 1, SCROLL_UP);
} else {
R++;
}
break;
case 7:
beep();
break;
case '\016': /* SMACS , as */
/* ^N, Shift out - Put G1 into GL */
gl = 1;
usedcharsets = true;
break;
case '\017': /* RMACS , ae */
/* ^O, Shift in - Put G0 into GL */
gl = 0;
usedcharsets = true;
break;
default: {
int thisgl = gl;
if (onegl >= 0) {
thisgl = onegl;
onegl = -1;
}
lastwaslf = 0;
if (c < 32) {
if (c != 0) {
if (debug > 0) {
debug("TSTATE_DATA char: " + ((int) c));
}
}
/* break; some BBS really want those characters, like hearst etc. */
if (c == 0) {
break;
}
}
if (C >= columns) {
if (wraparound) {
int bot = rows;
// If we're in the scroll region, check against the bottom margin
if (R <= getBottomMargin() && R >= getTopMargin()) {
bot = getBottomMargin() + 1;
}
if (R < bot - 1) {
R++;
} else {
if (debug > 3) {
debug("scrolling due to wrap at " + R);
}
insertLine(R, 1, SCROLL_UP);
}
C = 0;
} else {
// cursor stays on last character.
C = columns - 1;
}
}
boolean mapped = false;
// Mapping if DEC Special is chosen charset
if (usedcharsets) {
if (c >= '\u0020' && c <= '\u007f') {
switch (gx[thisgl]) {
case '0':
// Remap SCOANSI line drawing to VT100 line drawing chars
// for our SCO using customers.
if (terminalID.equals("scoansi") || terminalID.equals("ansi")) {
for (int i = 0; i < scoansi_acs.length(); i += 2) {
if (c == scoansi_acs.charAt(i)) {
c = scoansi_acs.charAt(i + 1);
break;
}
}
}
if (c >= '\u005f' && c <= '\u007e') {
c = DECSPECIAL[(short) c - 0x5f];
mapped = true;
}
break;
case '<': // 'user preferred' is currently 'ISO Latin-1 suppl
c = (char) ((c & 0x7f) | 0x80);
mapped = true;
break;
case 'A':
case 'B': // Latin-1 , ASCII -> fall through
mapped = true;
break;
default:
debug("Unsupported GL mapping: " + gx[thisgl]);
break;
}
}
if (!mapped && (c >= '\u0080' && c <= '\u00ff')) {
switch (gx[gr]) {
case '0':
if (c >= '\u00df' && c <= '\u00fe') {
c = DECSPECIAL[c - '\u00df'];
mapped = true;
}
break;
case '<':
case 'A':
case 'B':
mapped = true;
break;
default:
debug("Unsupported GR mapping: " + gx[gr]);
break;
}
}
}
if (!mapped && useibmcharset) {
c = map_cp850_unicode(c);
}
/* if(true || (statusmode == 0)) { */
if (isWide) {
if (C >= columns - 1) {
if (wraparound) {
int bot = rows;
// If we're in the scroll region, check against the bottom margin
if (R <= getBottomMargin() && R >= getTopMargin()) {
bot = getBottomMargin() + 1;
}
if (R < bot - 1) {
R++;
} else {
if (debug > 3) {
debug("scrolling due to wrap at " + R);
}
insertLine(R, 1, SCROLL_UP);
}
C = 0;
} else {
// cursor stays on last wide character.
C = columns - 2;
}
}
}
if (insertmode == 1) {
if (isWide) {
insertChar(C++, R, c, attributes | FULLWIDTH);
insertChar(C, R, ' ', attributes | FULLWIDTH);
} else {
insertChar(C, R, c, attributes);
}
} else {
if (isWide) {
putChar(C++, R, c, attributes | FULLWIDTH);
putChar(C, R, ' ', attributes | FULLWIDTH);
} else {
putChar(C, R, c, attributes);
}
}
/*
* } else { if (insertmode==1) { insertChar(C, rows, c, attributes); } else { putChar(C,
* rows, c, attributes); } }
*/
C++;
break;
}
} /* switch(c) */
break;
case TSTATE_OSC:
if ((c < 0x20) && (c != ESC)) {// NP - No printing character
handle_osc(osc);
term_state = TSTATE_DATA;
break;
}
// but check for vt102 ESC \
if (c == '\\' && osc.charAt(osc.length() - 1) == ESC) {
handle_osc(osc);
term_state = TSTATE_DATA;
break;
}
osc = osc + c;
break;
case TSTATE_ESCSPACE:
term_state = TSTATE_DATA;
switch (c) {
case 'F': /* S7C1T, Disable output of 8-bit controls, use 7-bit */
output8bit = false;
break;
case 'G': /* S8C1T, Enable output of 8-bit control codes */
output8bit = true;
break;
default:
debug("ESC " + c + " unhandled.");
}
break;
case TSTATE_ESC:
term_state = TSTATE_DATA;
switch (c) {
case ' ':
term_state = TSTATE_ESCSPACE;
break;
case '#':
term_state = TSTATE_ESCSQUARE;
break;
case 'c':
/* Hard terminal reset */
reset();
break;
case '[':
DCEvar = 0;
DCEvars[0] = 0;
DCEvars[1] = 0;
DCEvars[2] = 0;
DCEvars[3] = 0;
term_state = TSTATE_CSI;
break;
case ']':
osc = "";
term_state = TSTATE_OSC;
break;
case 'P':
dcs = "";
term_state = TSTATE_DCS;
break;
case 'A': /* CUU */
R--;
if (R < 0) {
R = 0;
}
break;
case 'B': /* CUD */
R++;
if (R >= rows) {
R = rows - 1;
}
break;
case 'C':
C++;
if (C >= columns) {
C = columns - 1;
}
break;
case 'I': // RI
insertLine(R, 1, SCROLL_DOWN);
break;
case 'E': /* NEL */
if (R == getBottomMargin() || R == rows - 1) {
insertLine(R, 1, SCROLL_UP);
} else {
R++;
}
C = 0;
if (debug > 1) {
debug("ESC E (at " + R + ")");
}
break;
case 'D': /* IND */
if (R == getBottomMargin() || R == rows - 1) {
insertLine(R, 1, SCROLL_UP);
} else {
R++;
}
if (debug > 1) {
debug("ESC D (at " + R + " )");
}
break;
case 'J': /* erase to end of screen */
if (R < rows - 1) {
deleteArea(0, R + 1, columns, rows - R - 1, attributes);
}
if (C < columns - 1) {
deleteArea(C, R, columns - C, 1, attributes);
}
break;
case 'K':
if (C < columns - 1) {
deleteArea(C, R, columns - C, 1, attributes);
}
break;
case 'M': // RI
debug("ESC M : R is " + R + ", tm is " + getTopMargin() + ", bm is " + getBottomMargin());
if (R > getTopMargin()) { // just go up 1 line.
R--;
} else { // scroll down
insertLine(R, 1, SCROLL_DOWN);
}
/* else do nothing ; */
if (debug > 2) {
debug("ESC M ");
}
break;
case 'H':
if (debug > 1) {
debug("ESC H at " + C);
}
/* right border probably ... */
if (C >= columns) {
C = columns - 1;
}
Tabs[C] = 1;
break;
case 'N': // SS2
onegl = 2;
break;
case 'O': // SS3
onegl = 3;
break;
case '=':
/* application keypad */
if (debug > 0) {
debug("ESC =");
}
keypadmode = true;
break;
case '<': /* vt52 mode off */
vt52mode = false;
break;
case '>': /* normal keypad */
if (debug > 0) {
debug("ESC >");
}
keypadmode = false;
break;
case '7': /* DECSC: save cursor, attributes */
Sc = C;
Sr = R;
Sgl = gl;
Sgr = gr;
Sa = attributes;
Sgx = new char[4];
for (int i = 0; i < 4; i++) {
Sgx[i] = gx[i];
}
if (debug > 1) {
debug("ESC 7");
}
break;
case '8': /* DECRC: restore cursor, attributes */
C = Sc;
R = Sr;
gl = Sgl;
gr = Sgr;
if (Sgx != null) {
for (int i = 0; i < 4; i++) {
gx[i] = Sgx[i];
}
}
attributes = Sa;
if (debug > 1) {
debug("ESC 8");
}
break;
case '(': /* Designate G0 Character set (ISO 2022) */
term_state = TSTATE_SETG0;
usedcharsets = true;
break;
case ')': /* Designate G1 character set (ISO 2022) */
term_state = TSTATE_SETG1;
usedcharsets = true;
break;
case '*': /* Designate G2 Character set (ISO 2022) */
term_state = TSTATE_SETG2;
usedcharsets = true;
break;
case '+': /* Designate G3 Character set (ISO 2022) */
term_state = TSTATE_SETG3;
usedcharsets = true;
break;
case '~': /* Locking Shift 1, right */
gr = 1;
usedcharsets = true;
break;
case 'n': /* Locking Shift 2 */
gl = 2;
usedcharsets = true;
break;
case '}': /* Locking Shift 2, right */
gr = 2;
usedcharsets = true;
break;
case 'o': /* Locking Shift 3 */
gl = 3;
usedcharsets = true;
break;
case '|': /* Locking Shift 3, right */
gr = 3;
usedcharsets = true;
break;
case 'Y': /* vt52 cursor address mode , next chars are x,y */
term_state = TSTATE_VT52Y;
break;
case '_':
term_state = TSTATE_TITLE;
break;
case '\\':
// TODO save title
term_state = TSTATE_DATA;
break;
default:
debug("ESC unknown letter: " + c + " (" + ((int) c) + ")");
break;
}
break;
case TSTATE_VT52X:
C = c - 37;
if (C < 0) {
C = 0;
} else if (C >= width) {
C = width - 1;
}
term_state = TSTATE_VT52Y;
break;
case TSTATE_VT52Y:
R = c - 37;
if (R < 0) {
R = 0;
} else if (R >= height) {
R = height - 1;
}
term_state = TSTATE_DATA;
break;
case TSTATE_SETG0:
if (c != '0' && c != 'A' && c != 'B' && c != '<') {
debug("ESC ( " + c + ": G0 char set? (" + ((int) c) + ")");
} else {
if (debug > 2) {
debug("ESC ( : G0 char set (" + c + " " + ((int) c) + ")");
}
gx[0] = c;
}
term_state = TSTATE_DATA;
break;
case TSTATE_SETG1:
if (c != '0' && c != 'A' && c != 'B' && c != '<') {
debug("ESC ) " + c + " (" + ((int) c) + ") :G1 char set?");
} else {
if (debug > 2) {
debug("ESC ) :G1 char set (" + c + " " + ((int) c) + ")");
}
gx[1] = c;
}
term_state = TSTATE_DATA;
break;
case TSTATE_SETG2:
if (c != '0' && c != 'A' && c != 'B' && c != '<') {
debug("ESC*:G2 char set? (" + ((int) c) + ")");
} else {
if (debug > 2) {
debug("ESC*:G2 char set (" + c + " " + ((int) c) + ")");
}
gx[2] = c;
}
term_state = TSTATE_DATA;
break;
case TSTATE_SETG3:
if (c != '0' && c != 'A' && c != 'B' && c != '<') {
debug("ESC+:G3 char set? (" + ((int) c) + ")");
} else {
if (debug > 2) {
debug("ESC+:G3 char set (" + c + " " + ((int) c) + ")");
}
gx[3] = c;
}
term_state = TSTATE_DATA;
break;
case TSTATE_ESCSQUARE:
switch (c) {
case '8':
for (int i = 0; i < columns; i++) {
for (int j = 0; j < rows; j++) {
putChar(i, j, 'E', 0);
}
}
break;
default:
debug("ESC # " + c + " not supported.");
break;
}
term_state = TSTATE_DATA;
break;
case TSTATE_DCS:
if (c == '\\' && dcs.charAt(dcs.length() - 1) == ESC) {
handle_dcs(dcs);
term_state = TSTATE_DATA;
break;
}
dcs = dcs + c;
break;
case TSTATE_DCEQ:
term_state = TSTATE_DATA;
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
term_state = TSTATE_DCEQ;
break;
case ';':
DCEvar++;
DCEvars[DCEvar] = 0;
term_state = TSTATE_DCEQ;
break;
case 's': // XTERM_SAVE missing!
if (true || debug > 1) {
debug("ESC [ ? " + DCEvars[0] + " s unimplemented!");
}
break;
case 'r': // XTERM_RESTORE
if (true || debug > 1) {
debug("ESC [ ? " + DCEvars[0] + " r");
}
/* DEC Mode reset */
for (int i = 0; i <= DCEvar; i++) {
switch (DCEvars[i]) {
case 3: /* 80 columns */
setScreenSize(80, height, true);
break;
case 4: /* scrolling mode, smooth */
break;
case 5: /* light background */
break;
case 6: /* DECOM (Origin Mode) move inside margins. */
moveoutsidemargins = true;
break;
case 7: /* DECAWM: Autowrap Mode */
wraparound = false;
break;
case 12:/* local echo off */
break;
case 9: /* X10 mouse */
case 1000: /* xterm style mouse report on */
case 1001:
case 1002:
case 1003:
mouserpt = DCEvars[i];
break;
default:
debug("ESC [ ? " + DCEvars[0] + " r, unimplemented!");
}
}
break;
case 'h': // DECSET
if (debug > 0) {
debug("ESC [ ? " + DCEvars[0] + " h");
}
/* DEC Mode set */
for (int i = 0; i <= DCEvar; i++) {
switch (DCEvars[i]) {
case 1: /* Application cursor keys */
KeyUp[0] = "\u001bOA";
KeyDown[0] = "\u001bOB";
KeyRight[0] = "\u001bOC";
KeyLeft[0] = "\u001bOD";
break;
case 2: /* DECANM */
vt52mode = false;
break;
case 3: /* 132 columns */
setScreenSize(132, height, true);
break;
case 6: /* DECOM: move inside margins. */
moveoutsidemargins = false;
break;
case 7: /* DECAWM: Autowrap Mode */
wraparound = true;
break;
case 25: /* turn cursor on */
showCursor(true);
break;
case 9: /* X10 mouse */
case 1000: /* xterm style mouse report on */
case 1001:
case 1002:
case 1003:
mouserpt = DCEvars[i];
break;
/* unimplemented stuff, fall through */
/* 4 - scrolling mode, smooth */
/* 5 - light background */
/* 12 - local echo off */
/* 18 - DECPFF - Printer Form Feed Mode -> On */
/* 19 - DECPEX - Printer Extent Mode -> Screen */
default:
debug("ESC [ ? " + DCEvars[0] + " h, unsupported.");
break;
}
}
break;
case 'i': // DEC Printer Control, autoprint, echo screenchars to printer
// This is different to CSI i!
// Also: "Autoprint prints a final display line only when the
// cursor is moved off the line by an autowrap or LF, FF, or
// VT (otherwise do not print the line)."
switch (DCEvars[0]) {
case 1:
if (debug > 1) {
debug("CSI ? 1 i : Print line containing cursor");
}
break;
case 4:
if (debug > 1) {
debug("CSI ? 4 i : Start passthrough printing");
}
break;
case 5:
if (debug > 1) {
debug("CSI ? 4 i : Stop passthrough printing");
}
break;
}
break;
case 'l': // DECRST
/* DEC Mode reset */
if (debug > 0) {
debug("ESC [ ? " + DCEvars[0] + " l");
}
for (int i = 0; i <= DCEvar; i++) {
switch (DCEvars[i]) {
case 1: /* Application cursor keys */
KeyUp[0] = "\u001b[A";
KeyDown[0] = "\u001b[B";
KeyRight[0] = "\u001b[C";
KeyLeft[0] = "\u001b[D";
break;
case 2: /* DECANM */
vt52mode = true;
break;
case 3: /* 80 columns */
setScreenSize(80, height, true);
break;
case 6: /* DECOM: move outside margins. */
moveoutsidemargins = true;
break;
case 7: /* DECAWM: Autowrap Mode OFF */
wraparound = false;
break;
case 25: /* turn cursor off */
showCursor(false);
break;
/* Unimplemented stuff: */
/* 4 - scrolling mode, jump */
/* 5 - dark background */
/* 7 - DECAWM - no wrap around mode */
/* 12 - local echo on */
/* 18 - DECPFF - Printer Form Feed Mode -> Off */
/* 19 - DECPEX - Printer Extent Mode -> Scrolling Region */
case 9: /* X10 mouse */
case 1000: /* xterm style mouse report OFF */
case 1001:
case 1002:
case 1003:
mouserpt = 0;
break;
default:
debug("ESC [ ? " + DCEvars[0] + " l, unsupported.");
break;
}
}
break;
case 'n':
if (debug > 0) {
debug("ESC [ ? " + DCEvars[0] + " n");
}
switch (DCEvars[0]) {
case 15:
/* printer? no printer. */
write((ESC) + "[?13n", false);
debug("ESC[5n");
break;
default:
debug("ESC [ ? " + DCEvars[0] + " n, unsupported.");
break;
}
break;
default:
debug("ESC [ ? " + DCEvars[0] + " " + c + ", unsupported.");
break;
}
break;
case TSTATE_CSI_EX:
term_state = TSTATE_DATA;
switch (c) {
case ESC:
term_state = TSTATE_ESC;
break;
default:
debug("Unknown character ESC[! character is " + (int) c);
break;
}
break;
case TSTATE_CSI_TICKS:
term_state = TSTATE_DATA;
switch (c) {
case 'p':
debug("Conformance level: " + DCEvars[0] + " (unsupported)," + DCEvars[1]);
if (DCEvars[0] == 61) {
output8bit = false;
break;
}
if (DCEvars[1] == 1) {
output8bit = false;
} else {
output8bit = true; /* 0 or 2 */
}
break;
default:
debug("Unknown ESC [... \"" + c);
break;
}
break;
case TSTATE_CSI_EQUAL:
term_state = TSTATE_DATA;
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
term_state = TSTATE_CSI_EQUAL;
break;
case ';':
DCEvar++;
DCEvars[DCEvar] = 0;
term_state = TSTATE_CSI_EQUAL;
break;
case 'F': /* SCO ANSI foreground */
{
int newcolor;
debug("ESC [ = " + DCEvars[0] + " F");
attributes &= ~COLOR_FG;
newcolor = ((DCEvars[0] & 1) << 2) | (DCEvars[0] & 2) | ((DCEvars[0] & 4) >> 2);
attributes |= (newcolor + 1) << COLOR_FG_SHIFT;
break;
}
case 'G': /* SCO ANSI background */
{
int newcolor;
debug("ESC [ = " + DCEvars[0] + " G");
attributes &= ~COLOR_BG;
newcolor = ((DCEvars[0] & 1) << 2) | (DCEvars[0] & 2) | ((DCEvars[0] & 4) >> 2);
attributes |= (newcolor + 1) << COLOR_BG_SHIFT;
break;
}
default:
debugStr.append("Unknown ESC [ = ");
for (int i = 0; i <= DCEvar; i++) {
debugStr.append(DCEvars[i]).append(',');
}
debugStr.append(c);
debug(debugStr.toString());
debugStr.setLength(0);
break;
}
break;
case TSTATE_CSI_DOLLAR:
term_state = TSTATE_DATA;
switch (c) {
case '}':
debug("Active Status Display now " + DCEvars[0]);
statusmode = DCEvars[0];
break;
/*
* bad documentation? case '-': debug("Set Status Display now "+DCEvars[0]); break;
*/
case '~':
debug("Status Line mode now " + DCEvars[0]);
break;
default:
debug("UNKNOWN Status Display code " + c + ", with Pn=" + DCEvars[0]);
break;
}
break;
case TSTATE_CSI:
term_state = TSTATE_DATA;
switch (c) {
case '"':
term_state = TSTATE_CSI_TICKS;
break;
case '$':
term_state = TSTATE_CSI_DOLLAR;
break;
case '=':
term_state = TSTATE_CSI_EQUAL;
break;
case '!':
term_state = TSTATE_CSI_EX;
break;
case '?':
DCEvar = 0;
DCEvars[0] = 0;
term_state = TSTATE_DCEQ;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
term_state = TSTATE_CSI;
break;
case ';':
DCEvar++;
DCEvars[DCEvar] = 0;
term_state = TSTATE_CSI;
break;
case 'c':/* send primary device attributes */
/* send (ESC[?61c) */
String subcode = "";
if (terminalID.equals("vt320")) {
subcode = "63;";
}
if (terminalID.equals("vt220")) {
subcode = "62;";
}
if (terminalID.equals("vt100")) {
subcode = "61;";
}
write((ESC) + "[?" + subcode + "1;2c", false);
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " c");
}
break;
case 'q':
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " q");
}
break;
case 'g':
/* used for tabsets */
switch (DCEvars[0]) {
case 3:/* clear them */
Tabs = new byte[width];
break;
case 0:
Tabs[C] = 0;
break;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " g");
}
break;
case 'h':
switch (DCEvars[0]) {
case 4:
insertmode = 1;
break;
case 20:
debug("Setting CRLF to TRUE");
sendcrlf = true;
break;
default:
debug("unsupported: ESC [ " + DCEvars[0] + " h");
break;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " h");
}
break;
case 'i': // Printer Controller mode.
// "Transparent printing sends all output, except the CSI 4 i
// termination string, to the printer and not the screen,
// uses an 8-bit channel if no parity so NUL and DEL will be
// seen by the printer and by the termination recognizer code,
// and all translation and character set selections are
// bypassed."
switch (DCEvars[0]) {
case 0:
if (debug > 1) {
debug("CSI 0 i: Print Screen, not implemented.");
}
break;
case 4:
if (debug > 1) {
debug("CSI 4 i: Enable Transparent Printing, not implemented.");
}
break;
case 5:
if (debug > 1) {
debug("CSI 4/5 i: Disable Transparent Printing, not implemented.");
}
break;
default:
debug("ESC [ " + DCEvars[0] + " i, unimplemented!");
}
break;
case 'l':
switch (DCEvars[0]) {
case 4:
insertmode = 0;
break;
case 20:
debug("Setting CRLF to FALSE");
sendcrlf = false;
break;
default:
debug("ESC [ " + DCEvars[0] + " l, unimplemented!");
break;
}
break;
case 'A': // CUU
{
int limit;
/* FIXME: xterm only cares about 0 and topmargin */
if (R >= getTopMargin()) {
limit = getTopMargin();
} else {
limit = 0;
}
if (DCEvars[0] == 0) {
R--;
} else {
R -= DCEvars[0];
}
if (R < limit) {
R = limit;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " A");
}
break;
}
case 'B': // CUD
/* cursor down n (1) times */
{
int limit;
if (R <= getBottomMargin()) {
limit = getBottomMargin();
} else {
limit = rows - 1;
}
if (DCEvars[0] == 0) {
R++;
} else {
R += DCEvars[0];
}
if (R > limit) {
R = limit;
} else {
if (debug > 2) {
debug("Not limited.");
}
}
if (debug > 2) {
debug("to: " + R);
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " B (at C=" + C + ")");
}
break;
}
case 'C':
if (DCEvars[0] == 0) {
DCEvars[0] = 1;
}
while (DCEvars[0]-- > 0) {
C++;
}
if (C >= columns) {
C = columns - 1;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " C");
}
break;
case 'd': // CVA
R = DCEvars[0];
if (R < 0) {
R = 0;
} else if (R >= height) {
R = height - 1;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " d");
}
break;
case 'D':
if (DCEvars[0] == 0) {
DCEvars[0] = 1;
}
while (DCEvars[0]-- > 0) {
C--;
}
if (C < 0) {
C = 0;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " D");
}
break;
case 'r': // DECSTBM
if (DCEvar > 0) // Ray: Any argument is optional
{
R = DCEvars[1] - 1;
if (R < 0) {
R = rows - 1;
} else if (R >= rows) {
R = rows - 1;
}
} else {
R = rows - 1;
}
int bot = R;
if (R >= DCEvars[0]) {
R = DCEvars[0] - 1;
if (R < 0) {
R = 0;
}
}
setMargins(R, bot);
_SetCursor(0, 0);
if (debug > 1) {
debug("ESC [" + DCEvars[0] + " ; " + DCEvars[1] + " r");
}
break;
case 'G': /* CUP / cursor absolute column */
C = DCEvars[0];
if (C < 0) {
C = 0;
} else if (C >= width) {
C = width - 1;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " G");
}
break;
case 'H': /* CUP / cursor position */
/* gets 2 arguments */
_SetCursor(DCEvars[0] - 1, DCEvars[1] - 1);
if (debug > 2) {
debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " H, moveoutsidemargins "
+ moveoutsidemargins);
debug(" -> R now " + R + ", C now " + C);
}
break;
case 'f': /* move cursor 2 */
/* gets 2 arguments */
R = DCEvars[0] - 1;
C = DCEvars[1] - 1;
if (C < 0) {
C = 0;
} else if (C >= width) {
C = width - 1;
}
if (R < 0) {
R = 0;
} else if (R >= height) {
R = height - 1;
}
if (debug > 2) {
debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " f");
}
break;
case 'S': /* ind aka 'scroll forward' */
if (DCEvars[0] == 0) {
insertLine(rows - 1, SCROLL_UP);
} else {
insertLine(rows - 1, DCEvars[0], SCROLL_UP);
}
break;
case 'L':
/* insert n lines */
if (DCEvars[0] == 0) {
insertLine(R, SCROLL_DOWN);
} else {
insertLine(R, DCEvars[0], SCROLL_DOWN);
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + "" + (c) + " (at R " + R + ")");
}
break;
case 'T': /* 'ri' aka scroll backward */
if (DCEvars[0] == 0) {
insertLine(0, SCROLL_DOWN);
} else {
insertLine(0, DCEvars[0], SCROLL_DOWN);
}
break;
case 'M':
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + "" + (c) + " at R=" + R);
}
if (DCEvars[0] == 0) {
deleteLine(R);
} else {
for (int i = 0; i < DCEvars[0]; i++) {
deleteLine(R);
}
}
break;
case 'K':
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " K");
}
/* clear in line */
switch (DCEvars[0]) {
case 6: /* 97801 uses ESC[6K for delete to end of line */
case 0:/* clear to right */
if (C < columns - 1) {
deleteArea(C, R, columns - C, 1, attributes);
}
break;
case 1:/* clear to the left, including this */
if (C > 0) {
deleteArea(0, R, C + 1, 1, attributes);
}
break;
case 2:/* clear whole line */
deleteArea(0, R, columns, 1, attributes);
break;
}
break;
case 'J':
/* clear below current line */
switch (DCEvars[0]) {
case 0:
if (R < rows - 1) {
deleteArea(0, R + 1, columns, rows - R - 1, attributes);
}
if (C < columns - 1) {
deleteArea(C, R, columns - C, 1, attributes);
}
break;
case 1:
if (R > 0) {
deleteArea(0, 0, columns, R, attributes);
}
if (C > 0) {
deleteArea(0, R, C + 1, 1, attributes);// include up to and including current
}
break;
case 2:
deleteArea(0, 0, columns, rows, attributes);
break;
}
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " J");
}
break;
case '@':
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " @");
}
for (int i = 0; i < DCEvars[0]; i++) {
insertChar(C, R, ' ', attributes);
}
break;
case 'X': {
int toerase = DCEvars[0];
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " X, C=" + C + ",R=" + R);
}
if (toerase == 0) {
toerase = 1;
}
if (toerase + C > columns) {
toerase = columns - C;
}
deleteArea(C, R, toerase, 1, attributes);
// does not change cursor position
break;
}
case 'P':
if (debug > 1) {
debug("ESC [ " + DCEvars[0] + " P, C=" + C + ",R=" + R);
}
if (DCEvars[0] == 0) {
DCEvars[0] = 1;
}
for (int i = 0; i < DCEvars[0]; i++) {
deleteChar(C, R);
}
break;
case 'n':
switch (DCEvars[0]) {
case 5: /* malfunction? No malfunction. */
writeSpecial((ESC) + "[0n");
if (debug > 1) {
debug("ESC[5n");
}
break;
case 6:
// DO NOT offset R and C by 1! (checked against /usr/X11R6/bin/resize
// FIXME check again.
// FIXME: but vttest thinks different???
writeSpecial((ESC) + "[" + R + ";" + C + "R");
if (debug > 1) {
debug("ESC[6n");
}
break;
default:
if (debug > 0) {
debug("ESC [ " + DCEvars[0] + " n??");
}
break;
}
break;
case 's': /* DECSC - save cursor */
Sc = C;
Sr = R;
Sa = attributes;
if (debug > 3) {
debug("ESC[s");
}
break;
case 'u': /* DECRC - restore cursor */
C = Sc;
R = Sr;
attributes = Sa;
if (debug > 3) {
debug("ESC[u");
}
break;
case 'm': /* attributes as color, bold , blink, */
if (debug > 3) {
debug("ESC [ ");
}
if (DCEvar == 0 && DCEvars[0] == 0) {
attributes = 0;
}
for (int i = 0; i <= DCEvar; i++) {
switch (DCEvars[i]) {
case 0:
if (DCEvar > 0) {
if (terminalID.equals("scoansi")) {
attributes &= COLOR; /* Keeps color. Strange but true. */
} else {
attributes = 0;
}
}
break;
case 1:
attributes |= BOLD;
attributes &= ~LOW;
break;
case 2:
/* SCO color hack mode */
if (terminalID.equals("scoansi") && ((DCEvar - i) >= 2)) {
int ncolor;
attributes &= ~(COLOR | BOLD);
ncolor = DCEvars[i + 1];
if ((ncolor & 8) == 8) {
attributes |= BOLD;
}
ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2);
attributes |= ((ncolor) + 1) << COLOR_FG_SHIFT;
ncolor = DCEvars[i + 2];
ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2);
attributes |= ((ncolor) + 1) << COLOR_BG_SHIFT;
i += 2;
} else {
attributes |= LOW;
}
break;
case 3: /* italics */
attributes |= INVERT;
break;
case 4:
attributes |= UNDERLINE;
break;
case 7:
attributes |= INVERT;
break;
case 8:
attributes |= INVISIBLE;
break;
case 5: /* blink on */
break;
/*
* 10 - ANSI X3.64-1979, select primary font, don't display control chars, don't set bit 8
* on output
*/
case 10:
gl = 0;
usedcharsets = true;
break;
/*
* 11 - ANSI X3.64-1979, select second alt. font, display control chars, set bit 8 on
* output
*/
case 11: /* SMACS , as */
case 12:
gl = 1;
usedcharsets = true;
break;
case 21: /* normal intensity */
attributes &= ~(LOW | BOLD);
break;
case 23: /* italics off */
attributes &= ~INVERT;
break;
case 25: /* blinking off */
break;
case 27:
attributes &= ~INVERT;
break;
case 28:
attributes &= ~INVISIBLE;
break;
case 24:
attributes &= ~UNDERLINE;
break;
case 22:
attributes &= ~BOLD;
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
attributes &= ~COLOR_FG;
attributes |= ((DCEvars[i] - 30) + 1) << COLOR_FG_SHIFT;
break;
case 38:
if (DCEvars[i + 1] == 5) {
attributes &= ~COLOR_FG;
attributes |= ((DCEvars[i + 2]) + 1) << COLOR_FG_SHIFT;
i += 2;
}
break;
case 39:
attributes &= ~COLOR_FG;
break;
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
attributes &= ~COLOR_BG;
attributes |= ((DCEvars[i] - 40) + 1) << COLOR_BG_SHIFT;
break;
case 48:
if (DCEvars[i + 1] == 5) {
attributes &= ~COLOR_BG;
attributes |= (DCEvars[i + 2] + 1) << COLOR_BG_SHIFT;
i += 2;
}
break;
case 49:
attributes &= ~COLOR_BG;
break;
case 90:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
case 97:
attributes &= ~COLOR_FG;
attributes |= ((DCEvars[i] - 82) + 1) << COLOR_FG_SHIFT;
break;
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
attributes &= ~COLOR_BG;
attributes |= ((DCEvars[i] - 92) + 1) << COLOR_BG_SHIFT;
break;
default:
debugStr.append("ESC [ ").append(DCEvars[i]).append(" m unknown...");
debug(debugStr.toString());
debugStr.setLength(0);
break;
}
if (debug > 3) {
debugStr.append(DCEvars[i]).append(';');
debug(debugStr.toString());
debugStr.setLength(0);
}
}
if (debug > 3) {
debugStr.append(" (attributes = ").append(attributes).append(")m");
debug(debugStr.toString());
debugStr.setLength(0);
}
break;
default:
debugStr.append("ESC [ unknown letter: ").append(c).append(" (").append((int) c)
.append(')');
debug(debugStr.toString());
debugStr.setLength(0);
break;
}
break;
case TSTATE_TITLE:
switch (c) {
case ESC:
term_state = TSTATE_ESC;
break;
default:
// TODO save title
break;
}
break;
default:
term_state = TSTATE_DATA;
break;
}
setCursorPosition(C, R);
}
/* hard reset the terminal */
public void reset() {
gx[0] = 'B';
gx[1] = 'B';
gx[2] = 'B';
gx[3] = 'B';
gl = 0; // default GL to G0
gr = 2; // default GR to G2
onegl = -1; // Single shift override
/* reset tabs */
int nw = width;
if (nw < 132) {
nw = 132;
}
Tabs = new byte[nw];
for (int i = 0; i < nw; i += 8) {
Tabs[i] = 1;
}
deleteArea(0, 0, width, height, attributes);
setMargins(0, height);
C = R = 0;
_SetCursor(0, 0);
if (display != null) {
display.resetColors();
}
showCursor(true);
/* FIXME: */
term_state = TSTATE_DATA;
}
}