/* * 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. * <P> * <B>Maintainer:</B> Marcus Meißner * * @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $ * @author Matthias L. Jugel, Marcus Meißner */ @SuppressWarnings("unused") public abstract class vt320 extends VDUBuffer implements VDUInput { /** * The current version id tag. * <P> * $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 <space> */ 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 <michelsu@microsoft.com> // # Lori Hoerth <lorih@microsoft.com> // # 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 <space> " + 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; } }