Home | History | Annotate | Download | only in terminal
      1 /*
      2  * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform".
      3  *
      4  * (c) Matthias L. Jugel, Marcus Meiner 1996-2005. All Rights Reserved.
      5  *
      6  * Please visit http://javatelnet.org/ for updates and contact.
      7  *
      8  * --LICENSE NOTICE--
      9  * This program is free software; you can redistribute it and/or
     10  * modify it under the terms of the GNU General Public License
     11  * as published by the Free Software Foundation; either version 2
     12  * of the License, or (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     22  * --LICENSE NOTICE--
     23  *
     24  */
     25 
     26 package de.mud.terminal;
     27 
     28 import java.util.Properties;
     29 
     30 /**
     31  * Implementation of a VT terminal emulation plus ANSI compatible.
     32  * <P>
     33  * <B>Maintainer:</B> Marcus Meiner
     34  *
     35  * @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $
     36  */
     37 @SuppressWarnings("unused")
     38 public abstract class vt320 extends VDUBuffer implements VDUInput {
     39 
     40   /**
     41    * The current version id tag.
     42    * <P>
     43    * $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $
     44    *
     45    */
     46   public final static String ID = "$Id: vt320.java 507 2005-10-25 10:14:52Z marcus $";
     47 
     48   /** the debug level */
     49   private final static int debug = 0;
     50   private StringBuilder debugStr;
     51 
     52   public abstract void debug(String notice);
     53 
     54   /**
     55    * Write an answer back to the remote host. This is needed to be able to send terminal answers
     56    * requests like status and type information.
     57    *
     58    * @param b
     59    *          the array of bytes to be sent
     60    */
     61   public abstract void write(byte[] b);
     62 
     63   /**
     64    * Write an answer back to the remote host. This is needed to be able to send terminal answers
     65    * requests like status and type information.
     66    *
     67    * @param b
     68    *          the array of bytes to be sent
     69    */
     70   public abstract void write(int b);
     71 
     72   /**
     73    * Play the beep sound ...
     74    */
     75   public void beep() { /* do nothing by default */
     76   }
     77 
     78   /**
     79    * Convenience function for putString(char[], int, int)
     80    */
     81   public void putString(String s) {
     82     int len = s.length();
     83     char[] tmp = new char[len];
     84     s.getChars(0, len, tmp, 0);
     85     putString(tmp, null, 0, len);
     86   }
     87 
     88   /**
     89    * Put string at current cursor position. Moves cursor according to the String. Does NOT wrap.
     90    *
     91    * @param s
     92    *          character array
     93    * @param start
     94    *          place to start in array
     95    * @param len
     96    *          number of characters to process
     97    */
     98   public void putString(char[] s, byte[] fullwidths, int start, int len) {
     99     if (len > 0) {
    100       // markLine(R, 1);
    101       int lastChar = -1;
    102       char c;
    103       boolean isWide = false;
    104 
    105       for (int i = 0; i < len; i++) {
    106         c = s[start + i];
    107         // Shortcut for my favorite ASCII
    108         if (c <= 0x7F) {
    109           if (lastChar != -1) {
    110             putChar((char) lastChar, isWide, false);
    111           }
    112           lastChar = c;
    113           isWide = false;
    114         } else if (!Character.isLowSurrogate(c) && !Character.isHighSurrogate(c)) {
    115           if (Character.getType(c) == Character.NON_SPACING_MARK) {
    116             if (lastChar != -1) {
    117               char nc = Precomposer.precompose((char) lastChar, c);
    118               putChar(nc, isWide, false);
    119               lastChar = -1;
    120             }
    121           } else {
    122             if (lastChar != -1) {
    123               putChar((char) lastChar, isWide, false);
    124             }
    125             lastChar = c;
    126             if (fullwidths != null) {
    127               isWide = fullwidths[i] == 1;
    128             }
    129           }
    130         }
    131       }
    132 
    133       if (lastChar != -1) {
    134         putChar((char) lastChar, isWide, false);
    135       }
    136 
    137       setCursorPosition(C, R);
    138       redraw();
    139     }
    140   }
    141 
    142   protected void sendTelnetCommand(byte cmd) {
    143 
    144   }
    145 
    146   /**
    147    * Sent the changed window size from the terminal to all listeners.
    148    */
    149   protected void setWindowSize(int c, int r) {
    150     /* To be overridden by Terminal.java */
    151   }
    152 
    153   @Override
    154   public void setScreenSize(int c, int r, boolean broadcast) {
    155     // int oldrows = height;
    156 
    157     if (debug > 2) {
    158       if (debugStr == null) {
    159         debugStr = new StringBuilder();
    160       }
    161 
    162       debugStr.append("setscreensize (").append(c).append(',').append(r).append(',')
    163           .append(broadcast).append(')');
    164       debug(debugStr.toString());
    165       debugStr.setLength(0);
    166     }
    167 
    168     super.setScreenSize(c, r, false);
    169 
    170     boolean cursorChanged = false;
    171 
    172     // Don't let the cursor go off the screen.
    173     if (C >= c) {
    174       C = c - 1;
    175       cursorChanged = true;
    176     }
    177 
    178     if (R >= r) {
    179       R = r - 1;
    180       cursorChanged = true;
    181     }
    182 
    183     if (cursorChanged) {
    184       setCursorPosition(C, R);
    185       redraw();
    186     }
    187 
    188     if (broadcast) {
    189       setWindowSize(c, r); /* broadcast up */
    190     }
    191   }
    192 
    193   /**
    194    * Create a new vt320 terminal and intialize it with useful settings.
    195    */
    196   public vt320(int width, int height) {
    197     super(width, height);
    198 
    199     debugStr = new StringBuilder();
    200 
    201     setVMS(false);
    202     setIBMCharset(false);
    203     setTerminalID("vt320");
    204     setBufferSize(100);
    205     // setBorder(2, false);
    206 
    207     gx = new char[4];
    208     reset();
    209 
    210     /* top row of numpad */
    211     PF1 = "\u001bOP";
    212     PF2 = "\u001bOQ";
    213     PF3 = "\u001bOR";
    214     PF4 = "\u001bOS";
    215 
    216     /* the 3x2 keyblock on PC keyboards */
    217     Insert = new String[4];
    218     Remove = new String[4];
    219     KeyHome = new String[4];
    220     KeyEnd = new String[4];
    221     NextScn = new String[4];
    222     PrevScn = new String[4];
    223     Escape = new String[4];
    224     BackSpace = new String[4];
    225     TabKey = new String[4];
    226     Insert[0] = Insert[1] = Insert[2] = Insert[3] = "\u001b[2~";
    227     Remove[0] = Remove[1] = Remove[2] = Remove[3] = "\u001b[3~";
    228     PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[5~";
    229     NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[6~";
    230     KeyHome[0] = KeyHome[1] = KeyHome[2] = KeyHome[3] = "\u001b[H";
    231     KeyEnd[0] = KeyEnd[1] = KeyEnd[2] = KeyEnd[3] = "\u001b[F";
    232     Escape[0] = Escape[1] = Escape[2] = Escape[3] = "\u001b";
    233     if (vms) {
    234       BackSpace[1] = "" + (char) 10; // VMS shift deletes word back
    235       BackSpace[2] = "\u0018"; // VMS control deletes line back
    236       BackSpace[0] = BackSpace[3] = "\u007f"; // VMS other is delete
    237     } else {
    238       // BackSpace[0] = BackSpace[1] = BackSpace[2] = BackSpace[3] = "\b";
    239       // ConnectBot modifications.
    240       BackSpace[0] = "\b";
    241       BackSpace[1] = "\u007f";
    242       BackSpace[2] = "\u001b[3~";
    243       BackSpace[3] = "\u001b[2~";
    244     }
    245 
    246     /* some more VT100 keys */
    247     Find = "\u001b[1~";
    248     Select = "\u001b[4~";
    249     Help = "\u001b[28~";
    250     Do = "\u001b[29~";
    251 
    252     FunctionKey = new String[21];
    253     FunctionKey[0] = "";
    254     FunctionKey[1] = PF1;
    255     FunctionKey[2] = PF2;
    256     FunctionKey[3] = PF3;
    257     FunctionKey[4] = PF4;
    258     /* following are defined differently for vt220 / vt132 ... */
    259     FunctionKey[5] = "\u001b[15~";
    260     FunctionKey[6] = "\u001b[17~";
    261     FunctionKey[7] = "\u001b[18~";
    262     FunctionKey[8] = "\u001b[19~";
    263     FunctionKey[9] = "\u001b[20~";
    264     FunctionKey[10] = "\u001b[21~";
    265     FunctionKey[11] = "\u001b[23~";
    266     FunctionKey[12] = "\u001b[24~";
    267     FunctionKey[13] = "\u001b[25~";
    268     FunctionKey[14] = "\u001b[26~";
    269     FunctionKey[15] = Help;
    270     FunctionKey[16] = Do;
    271     FunctionKey[17] = "\u001b[31~";
    272     FunctionKey[18] = "\u001b[32~";
    273     FunctionKey[19] = "\u001b[33~";
    274     FunctionKey[20] = "\u001b[34~";
    275 
    276     FunctionKeyShift = new String[21];
    277     FunctionKeyAlt = new String[21];
    278     FunctionKeyCtrl = new String[21];
    279 
    280     for (int i = 0; i < 20; i++) {
    281       FunctionKeyShift[i] = "";
    282       FunctionKeyAlt[i] = "";
    283       FunctionKeyCtrl[i] = "";
    284     }
    285     FunctionKeyShift[15] = Find;
    286     FunctionKeyShift[16] = Select;
    287 
    288     TabKey[0] = "\u0009";
    289     TabKey[1] = "\u001bOP\u0009";
    290     TabKey[2] = TabKey[3] = "";
    291 
    292     KeyUp = new String[4];
    293     KeyUp[0] = "\u001b[A";
    294     KeyDown = new String[4];
    295     KeyDown[0] = "\u001b[B";
    296     KeyRight = new String[4];
    297     KeyRight[0] = "\u001b[C";
    298     KeyLeft = new String[4];
    299     KeyLeft[0] = "\u001b[D";
    300     Numpad = new String[10];
    301     Numpad[0] = "\u001bOp";
    302     Numpad[1] = "\u001bOq";
    303     Numpad[2] = "\u001bOr";
    304     Numpad[3] = "\u001bOs";
    305     Numpad[4] = "\u001bOt";
    306     Numpad[5] = "\u001bOu";
    307     Numpad[6] = "\u001bOv";
    308     Numpad[7] = "\u001bOw";
    309     Numpad[8] = "\u001bOx";
    310     Numpad[9] = "\u001bOy";
    311     KPMinus = PF4;
    312     KPComma = "\u001bOl";
    313     KPPeriod = "\u001bOn";
    314     KPEnter = "\u001bOM";
    315 
    316     NUMPlus = new String[4];
    317     NUMPlus[0] = "+";
    318     NUMDot = new String[4];
    319     NUMDot[0] = ".";
    320   }
    321 
    322   public void setBackspace(int type) {
    323     switch (type) {
    324     case DELETE_IS_DEL:
    325       BackSpace[0] = "\u007f";
    326       BackSpace[1] = "\b";
    327       break;
    328     case DELETE_IS_BACKSPACE:
    329       BackSpace[0] = "\b";
    330       BackSpace[1] = "\u007f";
    331       break;
    332     }
    333   }
    334 
    335   /**
    336    * Create a default vt320 terminal with 80 columns and 24 lines.
    337    */
    338   public vt320() {
    339     this(80, 24);
    340   }
    341 
    342   /**
    343    * Terminal is mouse-aware and requires (x,y) coordinates of on the terminal (character
    344    * coordinates) and the button clicked.
    345    *
    346    * @param x
    347    * @param y
    348    * @param modifiers
    349    */
    350   public void mousePressed(int x, int y, int modifiers) {
    351     if (mouserpt == 0) {
    352       return;
    353     }
    354 
    355     int mods = modifiers;
    356     mousebut = 3;
    357     if ((mods & 16) == 16) {
    358       mousebut = 0;
    359     }
    360     if ((mods & 8) == 8) {
    361       mousebut = 1;
    362     }
    363     if ((mods & 4) == 4) {
    364       mousebut = 2;
    365     }
    366 
    367     int mousecode;
    368     if (mouserpt == 9) {
    369       mousecode = 0x20 | mousebut;
    370     } else {
    371       mousecode = mousebut | 0x20 | ((mods & 7) << 2);
    372     }
    373 
    374     byte b[] = new byte[6];
    375 
    376     b[0] = 27;
    377     b[1] = (byte) '[';
    378     b[2] = (byte) 'M';
    379     b[3] = (byte) mousecode;
    380     b[4] = (byte) (0x20 + x + 1);
    381     b[5] = (byte) (0x20 + y + 1);
    382 
    383     write(b); // FIXME: writeSpecial here
    384   }
    385 
    386   /**
    387    * Terminal is mouse-aware and requires the coordinates and button of the release.
    388    *
    389    * @param x
    390    * @param y
    391    * @param modifiers
    392    */
    393   public void mouseReleased(int x, int y, int modifiers) {
    394     if (mouserpt == 0) {
    395       return;
    396     }
    397 
    398     /*
    399      * problem is tht modifiers still have the released button set in them. int mods = modifiers;
    400      * mousebut = 3; if ((mods & 16)==16) mousebut=0; if ((mods & 8)==8 ) mousebut=1; if ((mods &
    401      * 4)==4 ) mousebut=2;
    402      */
    403 
    404     int mousecode;
    405     if (mouserpt == 9) {
    406       mousecode = 0x20 + mousebut; /* same as press? appears so. */
    407     } else {
    408       mousecode = '#';
    409     }
    410 
    411     byte b[] = new byte[6];
    412     b[0] = 27;
    413     b[1] = (byte) '[';
    414     b[2] = (byte) 'M';
    415     b[3] = (byte) mousecode;
    416     b[4] = (byte) (0x20 + x + 1);
    417     b[5] = (byte) (0x20 + y + 1);
    418     write(b); // FIXME: writeSpecial here
    419     mousebut = 0;
    420   }
    421 
    422   /** we should do localecho (passed from other modules). false is default */
    423   private boolean localecho = false;
    424 
    425   /**
    426    * Enable or disable the local echo property of the terminal.
    427    *
    428    * @param echo
    429    *          true if the terminal should echo locally
    430    */
    431   public void setLocalEcho(boolean echo) {
    432     localecho = echo;
    433   }
    434 
    435   /**
    436    * Enable the VMS mode of the terminal to handle some things differently for VMS hosts.
    437    *
    438    * @param vms
    439    *          true for vms mode, false for normal mode
    440    */
    441   public void setVMS(boolean vms) {
    442     this.vms = vms;
    443   }
    444 
    445   /**
    446    * Enable the usage of the IBM character set used by some BBS's. Special graphical character are
    447    * available in this mode.
    448    *
    449    * @param ibm
    450    *          true to use the ibm character set
    451    */
    452   public void setIBMCharset(boolean ibm) {
    453     useibmcharset = ibm;
    454   }
    455 
    456   /**
    457    * Override the standard key codes used by the terminal emulation.
    458    *
    459    * @param codes
    460    *          a properties object containing key code definitions
    461    */
    462   public void setKeyCodes(Properties codes) {
    463     String res, prefixes[] = { "", "S", "C", "A" };
    464     int i;
    465 
    466     for (i = 0; i < 10; i++) {
    467       res = codes.getProperty("NUMPAD" + i);
    468       if (res != null) {
    469         Numpad[i] = unEscape(res);
    470       }
    471     }
    472     for (i = 1; i < 20; i++) {
    473       res = codes.getProperty("F" + i);
    474       if (res != null) {
    475         FunctionKey[i] = unEscape(res);
    476       }
    477       res = codes.getProperty("SF" + i);
    478       if (res != null) {
    479         FunctionKeyShift[i] = unEscape(res);
    480       }
    481       res = codes.getProperty("CF" + i);
    482       if (res != null) {
    483         FunctionKeyCtrl[i] = unEscape(res);
    484       }
    485       res = codes.getProperty("AF" + i);
    486       if (res != null) {
    487         FunctionKeyAlt[i] = unEscape(res);
    488       }
    489     }
    490     for (i = 0; i < 4; i++) {
    491       res = codes.getProperty(prefixes[i] + "PGUP");
    492       if (res != null) {
    493         PrevScn[i] = unEscape(res);
    494       }
    495       res = codes.getProperty(prefixes[i] + "PGDOWN");
    496       if (res != null) {
    497         NextScn[i] = unEscape(res);
    498       }
    499       res = codes.getProperty(prefixes[i] + "END");
    500       if (res != null) {
    501         KeyEnd[i] = unEscape(res);
    502       }
    503       res = codes.getProperty(prefixes[i] + "HOME");
    504       if (res != null) {
    505         KeyHome[i] = unEscape(res);
    506       }
    507       res = codes.getProperty(prefixes[i] + "INSERT");
    508       if (res != null) {
    509         Insert[i] = unEscape(res);
    510       }
    511       res = codes.getProperty(prefixes[i] + "REMOVE");
    512       if (res != null) {
    513         Remove[i] = unEscape(res);
    514       }
    515       res = codes.getProperty(prefixes[i] + "UP");
    516       if (res != null) {
    517         KeyUp[i] = unEscape(res);
    518       }
    519       res = codes.getProperty(prefixes[i] + "DOWN");
    520       if (res != null) {
    521         KeyDown[i] = unEscape(res);
    522       }
    523       res = codes.getProperty(prefixes[i] + "LEFT");
    524       if (res != null) {
    525         KeyLeft[i] = unEscape(res);
    526       }
    527       res = codes.getProperty(prefixes[i] + "RIGHT");
    528       if (res != null) {
    529         KeyRight[i] = unEscape(res);
    530       }
    531       res = codes.getProperty(prefixes[i] + "ESCAPE");
    532       if (res != null) {
    533         Escape[i] = unEscape(res);
    534       }
    535       res = codes.getProperty(prefixes[i] + "BACKSPACE");
    536       if (res != null) {
    537         BackSpace[i] = unEscape(res);
    538       }
    539       res = codes.getProperty(prefixes[i] + "TAB");
    540       if (res != null) {
    541         TabKey[i] = unEscape(res);
    542       }
    543       res = codes.getProperty(prefixes[i] + "NUMPLUS");
    544       if (res != null) {
    545         NUMPlus[i] = unEscape(res);
    546       }
    547       res = codes.getProperty(prefixes[i] + "NUMDECIMAL");
    548       if (res != null) {
    549         NUMDot[i] = unEscape(res);
    550       }
    551     }
    552   }
    553 
    554   /**
    555    * Set the terminal id used to identify this terminal.
    556    *
    557    * @param terminalID
    558    *          the id string
    559    */
    560   public void setTerminalID(String terminalID) {
    561     this.terminalID = terminalID;
    562 
    563     if (terminalID.equals("scoansi")) {
    564       FunctionKey[1] = "\u001b[M";
    565       FunctionKey[2] = "\u001b[N";
    566       FunctionKey[3] = "\u001b[O";
    567       FunctionKey[4] = "\u001b[P";
    568       FunctionKey[5] = "\u001b[Q";
    569       FunctionKey[6] = "\u001b[R";
    570       FunctionKey[7] = "\u001b[S";
    571       FunctionKey[8] = "\u001b[T";
    572       FunctionKey[9] = "\u001b[U";
    573       FunctionKey[10] = "\u001b[V";
    574       FunctionKey[11] = "\u001b[W";
    575       FunctionKey[12] = "\u001b[X";
    576       FunctionKey[13] = "\u001b[Y";
    577       FunctionKey[14] = "?";
    578       FunctionKey[15] = "\u001b[a";
    579       FunctionKey[16] = "\u001b[b";
    580       FunctionKey[17] = "\u001b[c";
    581       FunctionKey[18] = "\u001b[d";
    582       FunctionKey[19] = "\u001b[e";
    583       FunctionKey[20] = "\u001b[f";
    584       PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[I";
    585       NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[G";
    586       // more theoretically.
    587     }
    588   }
    589 
    590   public void setAnswerBack(String ab) {
    591     answerBack = unEscape(ab);
    592   }
    593 
    594   /**
    595    * Get the terminal id used to identify this terminal.
    596    */
    597   public String getTerminalID() {
    598     return terminalID;
    599   }
    600 
    601   /**
    602    * A small conveniance method thar converts the string to a byte array for sending.
    603    *
    604    * @param s
    605    *          the string to be sent
    606    */
    607   private boolean write(String s, boolean doecho) {
    608     if (debug > 2) {
    609       debugStr.append("write(|").append(s).append("|,").append(doecho);
    610       debug(debugStr.toString());
    611       debugStr.setLength(0);
    612     }
    613     if (s == null) {
    614       return true;
    615       /*
    616        * NOTE: getBytes() honours some locale, it *CONVERTS* the string. However, we output only
    617        * 7bit stuff towards the target, and *some* 8 bit control codes. We must not mess up the
    618        * latter, so we do hand by hand copy.
    619        */
    620     }
    621 
    622     byte arr[] = new byte[s.length()];
    623     for (int i = 0; i < s.length(); i++) {
    624       arr[i] = (byte) s.charAt(i);
    625     }
    626     write(arr);
    627 
    628     if (doecho) {
    629       putString(s);
    630     }
    631     return true;
    632   }
    633 
    634   private boolean write(int s, boolean doecho) {
    635     if (debug > 2) {
    636       debugStr.append("write(|").append(s).append("|,").append(doecho);
    637       debug(debugStr.toString());
    638       debugStr.setLength(0);
    639     }
    640 
    641     write(s);
    642 
    643     // TODO check if character is wide
    644     if (doecho) {
    645       putChar((char) s, false, false);
    646     }
    647     return true;
    648   }
    649 
    650   private boolean write(String s) {
    651     return write(s, localecho);
    652   }
    653 
    654   // ===================================================================
    655   // the actual terminal emulation code comes here:
    656   // ===================================================================
    657 
    658   private String terminalID = "vt320";
    659   private String answerBack = "Use Terminal.answerback to set ...\n";
    660 
    661   // X - COLUMNS, Y - ROWS
    662   int R, C;
    663   int attributes = 0;
    664 
    665   int Sc, Sr, Sa, Stm, Sbm;
    666   char Sgr, Sgl;
    667   char Sgx[];
    668 
    669   int insertmode = 0;
    670   int statusmode = 0;
    671   boolean vt52mode = false;
    672   boolean keypadmode = false; /* false - numeric, true - application */
    673   boolean output8bit = false;
    674   int normalcursor = 0;
    675   boolean moveoutsidemargins = true;
    676   boolean wraparound = true;
    677   boolean sendcrlf = true;
    678   boolean capslock = false;
    679   boolean numlock = false;
    680   int mouserpt = 0;
    681   byte mousebut = 0;
    682 
    683   boolean useibmcharset = false;
    684 
    685   int lastwaslf = 0;
    686   boolean usedcharsets = false;
    687 
    688   private final static char ESC = 27;
    689   private final static char IND = 132;
    690   private final static char NEL = 133;
    691   private final static char RI = 141;
    692   private final static char SS2 = 142;
    693   private final static char SS3 = 143;
    694   private final static char DCS = 144;
    695   private final static char HTS = 136;
    696   private final static char CSI = 155;
    697   private final static char OSC = 157;
    698   private final static int TSTATE_DATA = 0;
    699   private final static int TSTATE_ESC = 1; /* ESC */
    700   private final static int TSTATE_CSI = 2; /* ESC [ */
    701   private final static int TSTATE_DCS = 3; /* ESC P */
    702   private final static int TSTATE_DCEQ = 4; /* ESC [? */
    703   private final static int TSTATE_ESCSQUARE = 5; /* ESC # */
    704   private final static int TSTATE_OSC = 6; /* ESC ] */
    705   private final static int TSTATE_SETG0 = 7; /* ESC (? */
    706   private final static int TSTATE_SETG1 = 8; /* ESC )? */
    707   private final static int TSTATE_SETG2 = 9; /* ESC *? */
    708   private final static int TSTATE_SETG3 = 10; /* ESC +? */
    709   private final static int TSTATE_CSI_DOLLAR = 11; /* ESC [ Pn $ */
    710   private final static int TSTATE_CSI_EX = 12; /* ESC [ ! */
    711   private final static int TSTATE_ESCSPACE = 13; /* ESC <space> */
    712   private final static int TSTATE_VT52X = 14;
    713   private final static int TSTATE_VT52Y = 15;
    714   private final static int TSTATE_CSI_TICKS = 16;
    715   private final static int TSTATE_CSI_EQUAL = 17; /* ESC [ = */
    716   private final static int TSTATE_TITLE = 18; /* xterm title */
    717 
    718   /* Keys we support */
    719   public final static int KEY_PAUSE = 1;
    720   public final static int KEY_F1 = 2;
    721   public final static int KEY_F2 = 3;
    722   public final static int KEY_F3 = 4;
    723   public final static int KEY_F4 = 5;
    724   public final static int KEY_F5 = 6;
    725   public final static int KEY_F6 = 7;
    726   public final static int KEY_F7 = 8;
    727   public final static int KEY_F8 = 9;
    728   public final static int KEY_F9 = 10;
    729   public final static int KEY_F10 = 11;
    730   public final static int KEY_F11 = 12;
    731   public final static int KEY_F12 = 13;
    732   public final static int KEY_UP = 14;
    733   public final static int KEY_DOWN = 15;
    734   public final static int KEY_LEFT = 16;
    735   public final static int KEY_RIGHT = 17;
    736   public final static int KEY_PAGE_DOWN = 18;
    737   public final static int KEY_PAGE_UP = 19;
    738   public final static int KEY_INSERT = 20;
    739   public final static int KEY_DELETE = 21;
    740   public final static int KEY_BACK_SPACE = 22;
    741   public final static int KEY_HOME = 23;
    742   public final static int KEY_END = 24;
    743   public final static int KEY_NUM_LOCK = 25;
    744   public final static int KEY_CAPS_LOCK = 26;
    745   public final static int KEY_SHIFT = 27;
    746   public final static int KEY_CONTROL = 28;
    747   public final static int KEY_ALT = 29;
    748   public final static int KEY_ENTER = 30;
    749   public final static int KEY_NUMPAD0 = 31;
    750   public final static int KEY_NUMPAD1 = 32;
    751   public final static int KEY_NUMPAD2 = 33;
    752   public final static int KEY_NUMPAD3 = 34;
    753   public final static int KEY_NUMPAD4 = 35;
    754   public final static int KEY_NUMPAD5 = 36;
    755   public final static int KEY_NUMPAD6 = 37;
    756   public final static int KEY_NUMPAD7 = 38;
    757   public final static int KEY_NUMPAD8 = 39;
    758   public final static int KEY_NUMPAD9 = 40;
    759   public final static int KEY_DECIMAL = 41;
    760   public final static int KEY_ADD = 42;
    761   public final static int KEY_ESCAPE = 43;
    762 
    763   public final static int DELETE_IS_DEL = 0;
    764   public final static int DELETE_IS_BACKSPACE = 1;
    765 
    766   /*
    767    * The graphics charsets B - default ASCII A - ISO Latin 1 0 - DEC SPECIAL < - User defined ....
    768    */
    769   char gx[];
    770   char gl; // GL (left charset)
    771   char gr; // GR (right charset)
    772   int onegl; // single shift override for GL.
    773 
    774   // Map from scoansi linedrawing to DEC _and_ unicode (for the stuff which
    775   // is not in linedrawing). Got from experimenting with scoadmin.
    776   private final static String scoansi_acs =
    777       "Tm7k3x4u?kZl@mYjEnB\u2566DqCtAvM\u2550:\u2551N\u2557I\u2554;\u2557H\u255a0a<\u255d";
    778   // array to store DEC Special -> Unicode mapping
    779   // Unicode DEC Unicode name (DEC name)
    780   private static char DECSPECIAL[] = { '\u0040', // 5f blank
    781     '\u2666', // 60 black diamond
    782     '\u2592', // 61 grey square
    783     '\u2409', // 62 Horizontal tab (ht) pict. for control
    784     '\u240c', // 63 Form Feed (ff) pict. for control
    785     '\u240d', // 64 Carriage Return (cr) pict. for control
    786     '\u240a', // 65 Line Feed (lf) pict. for control
    787     '\u00ba', // 66 Masculine ordinal indicator
    788     '\u00b1', // 67 Plus or minus sign
    789     '\u2424', // 68 New Line (nl) pict. for control
    790     '\u240b', // 69 Vertical Tab (vt) pict. for control
    791     '\u2518', // 6a Forms light up and left
    792     '\u2510', // 6b Forms light down and left
    793     '\u250c', // 6c Forms light down and right
    794     '\u2514', // 6d Forms light up and right
    795     '\u253c', // 6e Forms light vertical and horizontal
    796     '\u2594', // 6f Upper 1/8 block (Scan 1)
    797     '\u2580', // 70 Upper 1/2 block (Scan 3)
    798     '\u2500', // 71 Forms light horizontal or ?em dash? (Scan 5)
    799     '\u25ac', // 72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7)
    800     '\u005f', // 73 \u005f underscore or \u2581 lower 1/8 (Scan 9)
    801     '\u251c', // 74 Forms light vertical and right
    802     '\u2524', // 75 Forms light vertical and left
    803     '\u2534', // 76 Forms light up and horizontal
    804     '\u252c', // 77 Forms light down and horizontal
    805     '\u2502', // 78 vertical bar
    806     '\u2264', // 79 less than or equal
    807     '\u2265', // 7a greater than or equal
    808     '\u00b6', // 7b paragraph
    809     '\u2260', // 7c not equal
    810     '\u00a3', // 7d Pound Sign (british)
    811     '\u00b7' // 7e Middle Dot
    812       };
    813 
    814   /** Strings to send on function key pressing */
    815   private String Numpad[];
    816   private String FunctionKey[];
    817   private String FunctionKeyShift[];
    818   private String FunctionKeyCtrl[];
    819   private String FunctionKeyAlt[];
    820   private String TabKey[];
    821   private String KeyUp[], KeyDown[], KeyLeft[], KeyRight[];
    822   private String KPMinus, KPComma, KPPeriod, KPEnter;
    823   private String PF1, PF2, PF3, PF4;
    824   private String Help, Do, Find, Select;
    825 
    826   private String KeyHome[], KeyEnd[], Insert[], Remove[], PrevScn[], NextScn[];
    827   private String Escape[], BackSpace[], NUMDot[], NUMPlus[];
    828 
    829   private String osc, dcs; /* to memorize OSC & DCS control sequence */
    830 
    831   /** vt320 state variable (internal) */
    832   private int term_state = TSTATE_DATA;
    833   /** in vms mode, set by Terminal.VMS property */
    834   private boolean vms = false;
    835   /** Tabulators */
    836   private byte[] Tabs;
    837   /** The list of integers as used by CSI */
    838   private int[] DCEvars = new int[30];
    839   private int DCEvar;
    840 
    841   /**
    842    * Replace escape code characters (backslash + identifier) with their respective codes.
    843    *
    844    * @param tmp
    845    *          the string to be parsed
    846    * @return a unescaped string
    847    */
    848   static String unEscape(String tmp) {
    849     int idx = 0, oldidx = 0;
    850     String cmd;
    851     // f.println("unescape("+tmp+")");
    852     cmd = "";
    853     while ((idx = tmp.indexOf('\\', oldidx)) >= 0 && ++idx <= tmp.length()) {
    854       cmd += tmp.substring(oldidx, idx - 1);
    855       if (idx == tmp.length()) {
    856         return cmd;
    857       }
    858       switch (tmp.charAt(idx)) {
    859       case 'b':
    860         cmd += "\b";
    861         break;
    862       case 'e':
    863         cmd += "\u001b";
    864         break;
    865       case 'n':
    866         cmd += "\n";
    867         break;
    868       case 'r':
    869         cmd += "\r";
    870         break;
    871       case 't':
    872         cmd += "\t";
    873         break;
    874       case 'v':
    875         cmd += "\u000b";
    876         break;
    877       case 'a':
    878         cmd += "\u0012";
    879         break;
    880       default:
    881         if ((tmp.charAt(idx) >= '0') && (tmp.charAt(idx) <= '9')) {
    882           int i;
    883           for (i = idx; i < tmp.length(); i++) {
    884             if ((tmp.charAt(i) < '0') || (tmp.charAt(i) > '9')) {
    885               break;
    886             }
    887           }
    888           cmd += (char) Integer.parseInt(tmp.substring(idx, i));
    889           idx = i - 1;
    890         } else {
    891           cmd += tmp.substring(idx, ++idx);
    892         }
    893         break;
    894       }
    895       oldidx = ++idx;
    896     }
    897     if (oldidx <= tmp.length()) {
    898       cmd += tmp.substring(oldidx);
    899     }
    900     return cmd;
    901   }
    902 
    903   /**
    904    * A small conveniance method thar converts a 7bit string to the 8bit version depending on
    905    * VT52/Output8Bit mode.
    906    *
    907    * @param s
    908    *          the string to be sent
    909    */
    910   private boolean writeSpecial(String s) {
    911     if (s == null) {
    912       return true;
    913     }
    914     if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == 'O'))) {
    915       if (vt52mode) {
    916         if ((s.charAt(2) >= 'P') && (s.charAt(2) <= 'S')) {
    917           s = "\u001b" + s.substring(2); /* ESC x */
    918         } else {
    919           s = "\u001b?" + s.substring(2); /* ESC ? x */
    920         }
    921       } else {
    922         if (output8bit) {
    923           s = "\u008f" + s.substring(2); /* SS3 x */
    924         } /* else keep string as it is */
    925       }
    926     }
    927     if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == '['))) {
    928       if (output8bit) {
    929         s = "\u009b" + s.substring(2); /* CSI ... */
    930       } /* else keep */
    931     }
    932     return write(s, false);
    933   }
    934 
    935   /**
    936    * main keytyping event handler...
    937    */
    938   public void keyPressed(int keyCode, char keyChar, int modifiers) {
    939     boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0;
    940     boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0;
    941     boolean alt = (modifiers & VDUInput.KEY_ALT) != 0;
    942 
    943     if (debug > 1) {
    944       debugStr.append("keyPressed(").append(keyCode).append(", ").append((int) keyChar)
    945           .append(", ").append(modifiers).append(')');
    946       debug(debugStr.toString());
    947       debugStr.setLength(0);
    948     }
    949 
    950     int xind;
    951     String fmap[];
    952     xind = 0;
    953     fmap = FunctionKey;
    954     if (shift) {
    955       fmap = FunctionKeyShift;
    956       xind = 1;
    957     }
    958     if (control) {
    959       fmap = FunctionKeyCtrl;
    960       xind = 2;
    961     }
    962     if (alt) {
    963       fmap = FunctionKeyAlt;
    964       xind = 3;
    965     }
    966 
    967     switch (keyCode) {
    968     case KEY_PAUSE:
    969       if (shift || control) {
    970         sendTelnetCommand((byte) 243); // BREAK
    971       }
    972       break;
    973     case KEY_F1:
    974       writeSpecial(fmap[1]);
    975       break;
    976     case KEY_F2:
    977       writeSpecial(fmap[2]);
    978       break;
    979     case KEY_F3:
    980       writeSpecial(fmap[3]);
    981       break;
    982     case KEY_F4:
    983       writeSpecial(fmap[4]);
    984       break;
    985     case KEY_F5:
    986       writeSpecial(fmap[5]);
    987       break;
    988     case KEY_F6:
    989       writeSpecial(fmap[6]);
    990       break;
    991     case KEY_F7:
    992       writeSpecial(fmap[7]);
    993       break;
    994     case KEY_F8:
    995       writeSpecial(fmap[8]);
    996       break;
    997     case KEY_F9:
    998       writeSpecial(fmap[9]);
    999       break;
   1000     case KEY_F10:
   1001       writeSpecial(fmap[10]);
   1002       break;
   1003     case KEY_F11:
   1004       writeSpecial(fmap[11]);
   1005       break;
   1006     case KEY_F12:
   1007       writeSpecial(fmap[12]);
   1008       break;
   1009     case KEY_UP:
   1010       writeSpecial(KeyUp[xind]);
   1011       break;
   1012     case KEY_DOWN:
   1013       writeSpecial(KeyDown[xind]);
   1014       break;
   1015     case KEY_LEFT:
   1016       writeSpecial(KeyLeft[xind]);
   1017       break;
   1018     case KEY_RIGHT:
   1019       writeSpecial(KeyRight[xind]);
   1020       break;
   1021     case KEY_PAGE_DOWN:
   1022       writeSpecial(NextScn[xind]);
   1023       break;
   1024     case KEY_PAGE_UP:
   1025       writeSpecial(PrevScn[xind]);
   1026       break;
   1027     case KEY_INSERT:
   1028       writeSpecial(Insert[xind]);
   1029       break;
   1030     case KEY_DELETE:
   1031       writeSpecial(Remove[xind]);
   1032       break;
   1033     case KEY_BACK_SPACE:
   1034       writeSpecial(BackSpace[xind]);
   1035       if (localecho) {
   1036         if (BackSpace[xind] == "\b") {
   1037           putString("\b \b"); // make the last char 'deleted'
   1038         } else {
   1039           putString(BackSpace[xind]); // echo it
   1040         }
   1041       }
   1042       break;
   1043     case KEY_HOME:
   1044       writeSpecial(KeyHome[xind]);
   1045       break;
   1046     case KEY_END:
   1047       writeSpecial(KeyEnd[xind]);
   1048       break;
   1049     case KEY_NUM_LOCK:
   1050       if (vms && control) {
   1051         writeSpecial(PF1);
   1052       }
   1053       if (!control) {
   1054         numlock = !numlock;
   1055       }
   1056       break;
   1057     case KEY_CAPS_LOCK:
   1058       capslock = !capslock;
   1059       return;
   1060     case KEY_SHIFT:
   1061     case KEY_CONTROL:
   1062     case KEY_ALT:
   1063       return;
   1064     default:
   1065       break;
   1066     }
   1067   }
   1068 
   1069   /*
   1070    * public void keyReleased(KeyEvent evt) { if (debug > 1) debug("keyReleased("+evt+")"); // ignore
   1071    * }
   1072    */
   1073   /**
   1074    * Handle key Typed events for the terminal, this will get all normal key types, but no
   1075    * shift/alt/control/numlock.
   1076    */
   1077   public void keyTyped(int keyCode, char keyChar, int modifiers) {
   1078     boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0;
   1079     boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0;
   1080     boolean alt = (modifiers & VDUInput.KEY_ALT) != 0;
   1081 
   1082     if (debug > 1) {
   1083       debug("keyTyped(" + keyCode + ", " + (int) keyChar + ", " + modifiers + ")");
   1084     }
   1085 
   1086     if (keyChar == '\t') {
   1087       if (shift) {
   1088         write(TabKey[1], false);
   1089       } else {
   1090         if (control) {
   1091           write(TabKey[2], false);
   1092         } else {
   1093           if (alt) {
   1094             write(TabKey[3], false);
   1095           } else {
   1096             write(TabKey[0], false);
   1097           }
   1098         }
   1099       }
   1100       return;
   1101     }
   1102     if (alt) {
   1103       write(((char) (keyChar | 0x80)));
   1104       return;
   1105     }
   1106 
   1107     if (((keyCode == KEY_ENTER) || (keyChar == 10)) && !control) {
   1108       write('\r');
   1109       if (localecho) {
   1110         putString("\r\n"); // bad hack
   1111       }
   1112       return;
   1113     }
   1114 
   1115     if ((keyCode == 10) && !control) {
   1116       debug("Sending \\r");
   1117       write('\r');
   1118       return;
   1119     }
   1120 
   1121     // FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @,
   1122     // so we can't just use it here... will probably break some other VMS
   1123     // codes. -Marcus
   1124     // if(((!vms && keyChar == '2') || keyChar == '@' || keyChar == ' ')
   1125     // && control)
   1126     if (((!vms && keyChar == '2') || keyChar == ' ') && control) {
   1127       write(0);
   1128     }
   1129 
   1130     if (vms) {
   1131       if (keyChar == 127 && !control) {
   1132         if (shift) {
   1133           writeSpecial(Insert[0]); // VMS shift delete = insert
   1134         } else {
   1135           writeSpecial(Remove[0]); // VMS delete = remove
   1136         }
   1137         return;
   1138       } else if (control) {
   1139         switch (keyChar) {
   1140         case '0':
   1141           writeSpecial(Numpad[0]);
   1142           return;
   1143         case '1':
   1144           writeSpecial(Numpad[1]);
   1145           return;
   1146         case '2':
   1147           writeSpecial(Numpad[2]);
   1148           return;
   1149         case '3':
   1150           writeSpecial(Numpad[3]);
   1151           return;
   1152         case '4':
   1153           writeSpecial(Numpad[4]);
   1154           return;
   1155         case '5':
   1156           writeSpecial(Numpad[5]);
   1157           return;
   1158         case '6':
   1159           writeSpecial(Numpad[6]);
   1160           return;
   1161         case '7':
   1162           writeSpecial(Numpad[7]);
   1163           return;
   1164         case '8':
   1165           writeSpecial(Numpad[8]);
   1166           return;
   1167         case '9':
   1168           writeSpecial(Numpad[9]);
   1169           return;
   1170         case '.':
   1171           writeSpecial(KPPeriod);
   1172           return;
   1173         case '-':
   1174         case 31:
   1175           writeSpecial(KPMinus);
   1176           return;
   1177         case '+':
   1178           writeSpecial(KPComma);
   1179           return;
   1180         case 10:
   1181           writeSpecial(KPEnter);
   1182           return;
   1183         case '/':
   1184           writeSpecial(PF2);
   1185           return;
   1186         case '*':
   1187           writeSpecial(PF3);
   1188           return;
   1189           /* NUMLOCK handled in keyPressed */
   1190         default:
   1191           break;
   1192         }
   1193         /*
   1194          * Now what does this do and how did it get here. -Marcus if (shift && keyChar < 32) {
   1195          * write(PF1+(char)(keyChar + 64)); return; }
   1196          */
   1197       }
   1198     }
   1199 
   1200     // FIXME: not used?
   1201     // String fmap[];
   1202     int xind;
   1203     xind = 0;
   1204     // fmap = FunctionKey;
   1205     if (shift) {
   1206       // fmap = FunctionKeyShift;
   1207       xind = 1;
   1208     }
   1209     if (control) {
   1210       // fmap = FunctionKeyCtrl;
   1211       xind = 2;
   1212     }
   1213     if (alt) {
   1214       // fmap = FunctionKeyAlt;
   1215       xind = 3;
   1216     }
   1217 
   1218     if (keyCode == KEY_ESCAPE) {
   1219       writeSpecial(Escape[xind]);
   1220       return;
   1221     }
   1222 
   1223     if ((modifiers & VDUInput.KEY_ACTION) != 0) {
   1224       switch (keyCode) {
   1225       case KEY_NUMPAD0:
   1226         writeSpecial(Numpad[0]);
   1227         return;
   1228       case KEY_NUMPAD1:
   1229         writeSpecial(Numpad[1]);
   1230         return;
   1231       case KEY_NUMPAD2:
   1232         writeSpecial(Numpad[2]);
   1233         return;
   1234       case KEY_NUMPAD3:
   1235         writeSpecial(Numpad[3]);
   1236         return;
   1237       case KEY_NUMPAD4:
   1238         writeSpecial(Numpad[4]);
   1239         return;
   1240       case KEY_NUMPAD5:
   1241         writeSpecial(Numpad[5]);
   1242         return;
   1243       case KEY_NUMPAD6:
   1244         writeSpecial(Numpad[6]);
   1245         return;
   1246       case KEY_NUMPAD7:
   1247         writeSpecial(Numpad[7]);
   1248         return;
   1249       case KEY_NUMPAD8:
   1250         writeSpecial(Numpad[8]);
   1251         return;
   1252       case KEY_NUMPAD9:
   1253         writeSpecial(Numpad[9]);
   1254         return;
   1255       case KEY_DECIMAL:
   1256         writeSpecial(NUMDot[xind]);
   1257         return;
   1258       case KEY_ADD:
   1259         writeSpecial(NUMPlus[xind]);
   1260         return;
   1261       }
   1262     }
   1263 
   1264     if (!((keyChar == 8) || (keyChar == 127) || (keyChar == '\r') || (keyChar == '\n'))) {
   1265       write(keyChar);
   1266       return;
   1267     }
   1268   }
   1269 
   1270   private void handle_dcs(String dcs) {
   1271     debugStr.append("DCS: ").append(dcs);
   1272     debug(debugStr.toString());
   1273     debugStr.setLength(0);
   1274   }
   1275 
   1276   private void handle_osc(String osc) {
   1277     if (osc.length() > 2 && osc.substring(0, 2).equals("4;")) {
   1278       // Define color palette
   1279       String[] colorData = osc.split(";");
   1280 
   1281       try {
   1282         int colorIndex = Integer.parseInt(colorData[1]);
   1283 
   1284         if ("rgb:".equals(colorData[2].substring(0, 4))) {
   1285           String[] rgb = colorData[2].substring(4).split("/");
   1286 
   1287           int red = Integer.parseInt(rgb[0].substring(0, 2), 16) & 0xFF;
   1288           int green = Integer.parseInt(rgb[1].substring(0, 2), 16) & 0xFF;
   1289           int blue = Integer.parseInt(rgb[2].substring(0, 2), 16) & 0xFF;
   1290           display.setColor(colorIndex, red, green, blue);
   1291         }
   1292       } catch (Exception e) {
   1293         debugStr.append("OSC: invalid color sequence encountered: ").append(osc);
   1294         debug(debugStr.toString());
   1295         debugStr.setLength(0);
   1296       }
   1297     } else {
   1298       debug("OSC: " + osc);
   1299     }
   1300   }
   1301 
   1302   private final static char unimap[] = {
   1303     // #
   1304     // # Name: cp437_DOSLatinUS to Unicode table
   1305     // # Unicode version: 1.1
   1306     // # Table version: 1.1
   1307     // # Table format: Format A
   1308     // # Date: 03/31/95
   1309     // # Authors: Michel Suignard <michelsu (at) microsoft.com>
   1310     // # Lori Hoerth <lorih (at) microsoft.com>
   1311     // # General notes: none
   1312     // #
   1313     // # Format: Three tab-separated columns
   1314     // # Column #1 is the cp1255_WinHebrew code (in hex)
   1315     // # Column #2 is the Unicode (in hex as 0xXXXX)
   1316     // # Column #3 is the Unicode name (follows a comment sign, '#')
   1317     // #
   1318     // # The entries are in cp437_DOSLatinUS order
   1319     // #
   1320 
   1321     0x0000, // #NULL
   1322     0x0001, // #START OF HEADING
   1323     0x0002, // #START OF TEXT
   1324     0x0003, // #END OF TEXT
   1325     0x0004, // #END OF TRANSMISSION
   1326     0x0005, // #ENQUIRY
   1327     0x0006, // #ACKNOWLEDGE
   1328     0x0007, // #BELL
   1329     0x0008, // #BACKSPACE
   1330     0x0009, // #HORIZONTAL TABULATION
   1331     0x000a, // #LINE FEED
   1332     0x000b, // #VERTICAL TABULATION
   1333     0x000c, // #FORM FEED
   1334     0x000d, // #CARRIAGE RETURN
   1335     0x000e, // #SHIFT OUT
   1336     0x000f, // #SHIFT IN
   1337     0x0010, // #DATA LINK ESCAPE
   1338     0x0011, // #DEVICE CONTROL ONE
   1339     0x0012, // #DEVICE CONTROL TWO
   1340     0x0013, // #DEVICE CONTROL THREE
   1341     0x0014, // #DEVICE CONTROL FOUR
   1342     0x0015, // #NEGATIVE ACKNOWLEDGE
   1343     0x0016, // #SYNCHRONOUS IDLE
   1344     0x0017, // #END OF TRANSMISSION BLOCK
   1345     0x0018, // #CANCEL
   1346     0x0019, // #END OF MEDIUM
   1347     0x001a, // #SUBSTITUTE
   1348     0x001b, // #ESCAPE
   1349     0x001c, // #FILE SEPARATOR
   1350     0x001d, // #GROUP SEPARATOR
   1351     0x001e, // #RECORD SEPARATOR
   1352     0x001f, // #UNIT SEPARATOR
   1353     0x0020, // #SPACE
   1354     0x0021, // #EXCLAMATION MARK
   1355     0x0022, // #QUOTATION MARK
   1356     0x0023, // #NUMBER SIGN
   1357     0x0024, // #DOLLAR SIGN
   1358     0x0025, // #PERCENT SIGN
   1359     0x0026, // #AMPERSAND
   1360     0x0027, // #APOSTROPHE
   1361     0x0028, // #LEFT PARENTHESIS
   1362     0x0029, // #RIGHT PARENTHESIS
   1363     0x002a, // #ASTERISK
   1364     0x002b, // #PLUS SIGN
   1365     0x002c, // #COMMA
   1366     0x002d, // #HYPHEN-MINUS
   1367     0x002e, // #FULL STOP
   1368     0x002f, // #SOLIDUS
   1369     0x0030, // #DIGIT ZERO
   1370     0x0031, // #DIGIT ONE
   1371     0x0032, // #DIGIT TWO
   1372     0x0033, // #DIGIT THREE
   1373     0x0034, // #DIGIT FOUR
   1374     0x0035, // #DIGIT FIVE
   1375     0x0036, // #DIGIT SIX
   1376     0x0037, // #DIGIT SEVEN
   1377     0x0038, // #DIGIT EIGHT
   1378     0x0039, // #DIGIT NINE
   1379     0x003a, // #COLON
   1380     0x003b, // #SEMICOLON
   1381     0x003c, // #LESS-THAN SIGN
   1382     0x003d, // #EQUALS SIGN
   1383     0x003e, // #GREATER-THAN SIGN
   1384     0x003f, // #QUESTION MARK
   1385     0x0040, // #COMMERCIAL AT
   1386     0x0041, // #LATIN CAPITAL LETTER A
   1387     0x0042, // #LATIN CAPITAL LETTER B
   1388     0x0043, // #LATIN CAPITAL LETTER C
   1389     0x0044, // #LATIN CAPITAL LETTER D
   1390     0x0045, // #LATIN CAPITAL LETTER E
   1391     0x0046, // #LATIN CAPITAL LETTER F
   1392     0x0047, // #LATIN CAPITAL LETTER G
   1393     0x0048, // #LATIN CAPITAL LETTER H
   1394     0x0049, // #LATIN CAPITAL LETTER I
   1395     0x004a, // #LATIN CAPITAL LETTER J
   1396     0x004b, // #LATIN CAPITAL LETTER K
   1397     0x004c, // #LATIN CAPITAL LETTER L
   1398     0x004d, // #LATIN CAPITAL LETTER M
   1399     0x004e, // #LATIN CAPITAL LETTER N
   1400     0x004f, // #LATIN CAPITAL LETTER O
   1401     0x0050, // #LATIN CAPITAL LETTER P
   1402     0x0051, // #LATIN CAPITAL LETTER Q
   1403     0x0052, // #LATIN CAPITAL LETTER R
   1404     0x0053, // #LATIN CAPITAL LETTER S
   1405     0x0054, // #LATIN CAPITAL LETTER T
   1406     0x0055, // #LATIN CAPITAL LETTER U
   1407     0x0056, // #LATIN CAPITAL LETTER V
   1408     0x0057, // #LATIN CAPITAL LETTER W
   1409     0x0058, // #LATIN CAPITAL LETTER X
   1410     0x0059, // #LATIN CAPITAL LETTER Y
   1411     0x005a, // #LATIN CAPITAL LETTER Z
   1412     0x005b, // #LEFT SQUARE BRACKET
   1413     0x005c, // #REVERSE SOLIDUS
   1414     0x005d, // #RIGHT SQUARE BRACKET
   1415     0x005e, // #CIRCUMFLEX ACCENT
   1416     0x005f, // #LOW LINE
   1417     0x0060, // #GRAVE ACCENT
   1418     0x0061, // #LATIN SMALL LETTER A
   1419     0x0062, // #LATIN SMALL LETTER B
   1420     0x0063, // #LATIN SMALL LETTER C
   1421     0x0064, // #LATIN SMALL LETTER D
   1422     0x0065, // #LATIN SMALL LETTER E
   1423     0x0066, // #LATIN SMALL LETTER F
   1424     0x0067, // #LATIN SMALL LETTER G
   1425     0x0068, // #LATIN SMALL LETTER H
   1426     0x0069, // #LATIN SMALL LETTER I
   1427     0x006a, // #LATIN SMALL LETTER J
   1428     0x006b, // #LATIN SMALL LETTER K
   1429     0x006c, // #LATIN SMALL LETTER L
   1430     0x006d, // #LATIN SMALL LETTER M
   1431     0x006e, // #LATIN SMALL LETTER N
   1432     0x006f, // #LATIN SMALL LETTER O
   1433     0x0070, // #LATIN SMALL LETTER P
   1434     0x0071, // #LATIN SMALL LETTER Q
   1435     0x0072, // #LATIN SMALL LETTER R
   1436     0x0073, // #LATIN SMALL LETTER S
   1437     0x0074, // #LATIN SMALL LETTER T
   1438     0x0075, // #LATIN SMALL LETTER U
   1439     0x0076, // #LATIN SMALL LETTER V
   1440     0x0077, // #LATIN SMALL LETTER W
   1441     0x0078, // #LATIN SMALL LETTER X
   1442     0x0079, // #LATIN SMALL LETTER Y
   1443     0x007a, // #LATIN SMALL LETTER Z
   1444     0x007b, // #LEFT CURLY BRACKET
   1445     0x007c, // #VERTICAL LINE
   1446     0x007d, // #RIGHT CURLY BRACKET
   1447     0x007e, // #TILDE
   1448     0x007f, // #DELETE
   1449     0x00c7, // #LATIN CAPITAL LETTER C WITH CEDILLA
   1450     0x00fc, // #LATIN SMALL LETTER U WITH DIAERESIS
   1451     0x00e9, // #LATIN SMALL LETTER E WITH ACUTE
   1452     0x00e2, // #LATIN SMALL LETTER A WITH CIRCUMFLEX
   1453     0x00e4, // #LATIN SMALL LETTER A WITH DIAERESIS
   1454     0x00e0, // #LATIN SMALL LETTER A WITH GRAVE
   1455     0x00e5, // #LATIN SMALL LETTER A WITH RING ABOVE
   1456     0x00e7, // #LATIN SMALL LETTER C WITH CEDILLA
   1457     0x00ea, // #LATIN SMALL LETTER E WITH CIRCUMFLEX
   1458     0x00eb, // #LATIN SMALL LETTER E WITH DIAERESIS
   1459     0x00e8, // #LATIN SMALL LETTER E WITH GRAVE
   1460     0x00ef, // #LATIN SMALL LETTER I WITH DIAERESIS
   1461     0x00ee, // #LATIN SMALL LETTER I WITH CIRCUMFLEX
   1462     0x00ec, // #LATIN SMALL LETTER I WITH GRAVE
   1463     0x00c4, // #LATIN CAPITAL LETTER A WITH DIAERESIS
   1464     0x00c5, // #LATIN CAPITAL LETTER A WITH RING ABOVE
   1465     0x00c9, // #LATIN CAPITAL LETTER E WITH ACUTE
   1466     0x00e6, // #LATIN SMALL LIGATURE AE
   1467     0x00c6, // #LATIN CAPITAL LIGATURE AE
   1468     0x00f4, // #LATIN SMALL LETTER O WITH CIRCUMFLEX
   1469     0x00f6, // #LATIN SMALL LETTER O WITH DIAERESIS
   1470     0x00f2, // #LATIN SMALL LETTER O WITH GRAVE
   1471     0x00fb, // #LATIN SMALL LETTER U WITH CIRCUMFLEX
   1472     0x00f9, // #LATIN SMALL LETTER U WITH GRAVE
   1473     0x00ff, // #LATIN SMALL LETTER Y WITH DIAERESIS
   1474     0x00d6, // #LATIN CAPITAL LETTER O WITH DIAERESIS
   1475     0x00dc, // #LATIN CAPITAL LETTER U WITH DIAERESIS
   1476     0x00a2, // #CENT SIGN
   1477     0x00a3, // #POUND SIGN
   1478     0x00a5, // #YEN SIGN
   1479     0x20a7, // #PESETA SIGN
   1480     0x0192, // #LATIN SMALL LETTER F WITH HOOK
   1481     0x00e1, // #LATIN SMALL LETTER A WITH ACUTE
   1482     0x00ed, // #LATIN SMALL LETTER I WITH ACUTE
   1483     0x00f3, // #LATIN SMALL LETTER O WITH ACUTE
   1484     0x00fa, // #LATIN SMALL LETTER U WITH ACUTE
   1485     0x00f1, // #LATIN SMALL LETTER N WITH TILDE
   1486     0x00d1, // #LATIN CAPITAL LETTER N WITH TILDE
   1487     0x00aa, // #FEMININE ORDINAL INDICATOR
   1488     0x00ba, // #MASCULINE ORDINAL INDICATOR
   1489     0x00bf, // #INVERTED QUESTION MARK
   1490     0x2310, // #REVERSED NOT SIGN
   1491     0x00ac, // #NOT SIGN
   1492     0x00bd, // #VULGAR FRACTION ONE HALF
   1493     0x00bc, // #VULGAR FRACTION ONE QUARTER
   1494     0x00a1, // #INVERTED EXCLAMATION MARK
   1495     0x00ab, // #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
   1496     0x00bb, // #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
   1497     0x2591, // #LIGHT SHADE
   1498     0x2592, // #MEDIUM SHADE
   1499     0x2593, // #DARK SHADE
   1500     0x2502, // #BOX DRAWINGS LIGHT VERTICAL
   1501     0x2524, // #BOX DRAWINGS LIGHT VERTICAL AND LEFT
   1502     0x2561, // #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
   1503     0x2562, // #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
   1504     0x2556, // #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
   1505     0x2555, // #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
   1506     0x2563, // #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
   1507     0x2551, // #BOX DRAWINGS DOUBLE VERTICAL
   1508     0x2557, // #BOX DRAWINGS DOUBLE DOWN AND LEFT
   1509     0x255d, // #BOX DRAWINGS DOUBLE UP AND LEFT
   1510     0x255c, // #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
   1511     0x255b, // #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
   1512     0x2510, // #BOX DRAWINGS LIGHT DOWN AND LEFT
   1513     0x2514, // #BOX DRAWINGS LIGHT UP AND RIGHT
   1514     0x2534, // #BOX DRAWINGS LIGHT UP AND HORIZONTAL
   1515     0x252c, // #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
   1516     0x251c, // #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
   1517     0x2500, // #BOX DRAWINGS LIGHT HORIZONTAL
   1518     0x253c, // #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
   1519     0x255e, // #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
   1520     0x255f, // #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
   1521     0x255a, // #BOX DRAWINGS DOUBLE UP AND RIGHT
   1522     0x2554, // #BOX DRAWINGS DOUBLE DOWN AND RIGHT
   1523     0x2569, // #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
   1524     0x2566, // #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
   1525     0x2560, // #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
   1526     0x2550, // #BOX DRAWINGS DOUBLE HORIZONTAL
   1527     0x256c, // #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
   1528     0x2567, // #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
   1529     0x2568, // #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
   1530     0x2564, // #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
   1531     0x2565, // #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
   1532     0x2559, // #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
   1533     0x2558, // #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
   1534     0x2552, // #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
   1535     0x2553, // #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
   1536     0x256b, // #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
   1537     0x256a, // #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
   1538     0x2518, // #BOX DRAWINGS LIGHT UP AND LEFT
   1539     0x250c, // #BOX DRAWINGS LIGHT DOWN AND RIGHT
   1540     0x2588, // #FULL BLOCK
   1541     0x2584, // #LOWER HALF BLOCK
   1542     0x258c, // #LEFT HALF BLOCK
   1543     0x2590, // #RIGHT HALF BLOCK
   1544     0x2580, // #UPPER HALF BLOCK
   1545     0x03b1, // #GREEK SMALL LETTER ALPHA
   1546     0x00df, // #LATIN SMALL LETTER SHARP S
   1547     0x0393, // #GREEK CAPITAL LETTER GAMMA
   1548     0x03c0, // #GREEK SMALL LETTER PI
   1549     0x03a3, // #GREEK CAPITAL LETTER SIGMA
   1550     0x03c3, // #GREEK SMALL LETTER SIGMA
   1551     0x00b5, // #MICRO SIGN
   1552     0x03c4, // #GREEK SMALL LETTER TAU
   1553     0x03a6, // #GREEK CAPITAL LETTER PHI
   1554     0x0398, // #GREEK CAPITAL LETTER THETA
   1555     0x03a9, // #GREEK CAPITAL LETTER OMEGA
   1556     0x03b4, // #GREEK SMALL LETTER DELTA
   1557     0x221e, // #INFINITY
   1558     0x03c6, // #GREEK SMALL LETTER PHI
   1559     0x03b5, // #GREEK SMALL LETTER EPSILON
   1560     0x2229, // #INTERSECTION
   1561     0x2261, // #IDENTICAL TO
   1562     0x00b1, // #PLUS-MINUS SIGN
   1563     0x2265, // #GREATER-THAN OR EQUAL TO
   1564     0x2264, // #LESS-THAN OR EQUAL TO
   1565     0x2320, // #TOP HALF INTEGRAL
   1566     0x2321, // #BOTTOM HALF INTEGRAL
   1567     0x00f7, // #DIVISION SIGN
   1568     0x2248, // #ALMOST EQUAL TO
   1569     0x00b0, // #DEGREE SIGN
   1570     0x2219, // #BULLET OPERATOR
   1571     0x00b7, // #MIDDLE DOT
   1572     0x221a, // #SQUARE ROOT
   1573     0x207f, // #SUPERSCRIPT LATIN SMALL LETTER N
   1574     0x00b2, // #SUPERSCRIPT TWO
   1575     0x25a0, // #BLACK SQUARE
   1576     0x00a0, // #NO-BREAK SPACE
   1577   };
   1578 
   1579   public char map_cp850_unicode(char x) {
   1580     if (x >= 0x100) {
   1581       return x;
   1582     }
   1583     return unimap[x];
   1584   }
   1585 
   1586   private void _SetCursor(int row, int col) {
   1587     int maxr = height - 1;
   1588     int tm = getTopMargin();
   1589 
   1590     R = (row < 0) ? 0 : row;
   1591     C = (col < 0) ? 0 : (col >= width) ? width - 1 : col;
   1592 
   1593     if (!moveoutsidemargins) {
   1594       R += tm;
   1595       maxr = getBottomMargin();
   1596     }
   1597     if (R > maxr) {
   1598       R = maxr;
   1599     }
   1600   }
   1601 
   1602   private void putChar(char c, boolean isWide, boolean doshowcursor) {
   1603     int rows = height; // statusline
   1604     int columns = width;
   1605     // byte msg[];
   1606 
   1607     // if (debug > 4) {
   1608     // debugStr.append("putChar(")
   1609     // .append(c)
   1610     // .append(" [")
   1611     // .append((int) c)
   1612     // .append("]) at R=")
   1613     // .append(R)
   1614     // .append(" , C=")
   1615     // .append(C)
   1616     // .append(", columns=")
   1617     // .append(columns)
   1618     // .append(", rows=")
   1619     // .append(rows);
   1620     // debug(debugStr.toString());
   1621     // debugStr.setLength(0);
   1622     // }
   1623     // markLine(R, 1);
   1624     // if (c > 255) {
   1625     // if (debug > 0)
   1626     // debug("char > 255:" + (int) c);
   1627     // //return;
   1628     // }
   1629 
   1630     switch (term_state) {
   1631     case TSTATE_DATA:
   1632       /*
   1633        * FIXME: we shouldn't use chars with bit 8 set if ibmcharset. probably... but some BBS do
   1634        * anyway...
   1635        */
   1636       if (!useibmcharset) {
   1637         boolean doneflag = true;
   1638         switch (c) {
   1639         case OSC:
   1640           osc = "";
   1641           term_state = TSTATE_OSC;
   1642           break;
   1643         case RI:
   1644           if (R > getTopMargin()) {
   1645             R--;
   1646           } else {
   1647             insertLine(R, 1, SCROLL_DOWN);
   1648           }
   1649           if (debug > 1) {
   1650             debug("RI");
   1651           }
   1652           break;
   1653         case IND:
   1654           if (debug > 2) {
   1655             debugStr.append("IND at ").append(R).append(", tm is ").append(getTopMargin())
   1656                 .append(", bm is ").append(getBottomMargin());
   1657             debug(debugStr.toString());
   1658             debugStr.setLength(0);
   1659           }
   1660           if (R == getBottomMargin() || R == rows - 1) {
   1661             insertLine(R, 1, SCROLL_UP);
   1662           } else {
   1663             R++;
   1664           }
   1665           if (debug > 1) {
   1666             debug("IND (at " + R + " )");
   1667           }
   1668           break;
   1669         case NEL:
   1670           if (R == getBottomMargin() || R == rows - 1) {
   1671             insertLine(R, 1, SCROLL_UP);
   1672           } else {
   1673             R++;
   1674           }
   1675           C = 0;
   1676           if (debug > 1) {
   1677             debug("NEL (at " + R + " )");
   1678           }
   1679           break;
   1680         case HTS:
   1681           Tabs[C] = 1;
   1682           if (debug > 1) {
   1683             debug("HTS");
   1684           }
   1685           break;
   1686         case DCS:
   1687           dcs = "";
   1688           term_state = TSTATE_DCS;
   1689           break;
   1690         default:
   1691           doneflag = false;
   1692           break;
   1693         }
   1694         if (doneflag) {
   1695           break;
   1696         }
   1697       }
   1698       switch (c) {
   1699       case SS3:
   1700         onegl = 3;
   1701         break;
   1702       case SS2:
   1703         onegl = 2;
   1704         break;
   1705       case CSI: // should be in the 8bit section, but some BBS use this
   1706         DCEvar = 0;
   1707         DCEvars[0] = 0;
   1708         DCEvars[1] = 0;
   1709         DCEvars[2] = 0;
   1710         DCEvars[3] = 0;
   1711         term_state = TSTATE_CSI;
   1712         break;
   1713       case ESC:
   1714         term_state = TSTATE_ESC;
   1715         lastwaslf = 0;
   1716         break;
   1717       case 5: /* ENQ */
   1718         write(answerBack, false);
   1719         break;
   1720       case 12:
   1721         /* FormFeed, Home for the BBS world */
   1722         deleteArea(0, 0, columns, rows, attributes);
   1723         C = R = 0;
   1724         break;
   1725       case '\b': /* 8 */
   1726         C--;
   1727         if (C < 0) {
   1728           C = 0;
   1729         }
   1730         lastwaslf = 0;
   1731         break;
   1732       case '\t':
   1733         do {
   1734           // Don't overwrite or insert! TABS are not destructive, but movement!
   1735           C++;
   1736         } while (C < columns && (Tabs[C] == 0));
   1737         lastwaslf = 0;
   1738         break;
   1739       case '\r': // 13 CR
   1740         C = 0;
   1741         break;
   1742       case '\n': // 10 LF
   1743         if (debug > 3) {
   1744           debug("R= " + R + ", bm " + getBottomMargin() + ", tm=" + getTopMargin() + ", rows="
   1745               + rows);
   1746         }
   1747         if (!vms) {
   1748           if (lastwaslf != 0 && lastwaslf != c) {
   1749             break;
   1750           }
   1751           lastwaslf = c;
   1752           /* C = 0; */
   1753         }
   1754         if (R == getBottomMargin() || R >= rows - 1) {
   1755           insertLine(R, 1, SCROLL_UP);
   1756         } else {
   1757           R++;
   1758         }
   1759         break;
   1760       case 7:
   1761         beep();
   1762         break;
   1763       case '\016': /* SMACS , as */
   1764         /* ^N, Shift out - Put G1 into GL */
   1765         gl = 1;
   1766         usedcharsets = true;
   1767         break;
   1768       case '\017': /* RMACS , ae */
   1769         /* ^O, Shift in - Put G0 into GL */
   1770         gl = 0;
   1771         usedcharsets = true;
   1772         break;
   1773       default: {
   1774         int thisgl = gl;
   1775 
   1776         if (onegl >= 0) {
   1777           thisgl = onegl;
   1778           onegl = -1;
   1779         }
   1780         lastwaslf = 0;
   1781         if (c < 32) {
   1782           if (c != 0) {
   1783             if (debug > 0) {
   1784               debug("TSTATE_DATA char: " + ((int) c));
   1785             }
   1786           }
   1787           /* break; some BBS really want those characters, like hearst etc. */
   1788           if (c == 0) {
   1789             break;
   1790           }
   1791         }
   1792         if (C >= columns) {
   1793           if (wraparound) {
   1794             int bot = rows;
   1795 
   1796             // If we're in the scroll region, check against the bottom margin
   1797             if (R <= getBottomMargin() && R >= getTopMargin()) {
   1798               bot = getBottomMargin() + 1;
   1799             }
   1800 
   1801             if (R < bot - 1) {
   1802               R++;
   1803             } else {
   1804               if (debug > 3) {
   1805                 debug("scrolling due to wrap at " + R);
   1806               }
   1807               insertLine(R, 1, SCROLL_UP);
   1808             }
   1809             C = 0;
   1810           } else {
   1811             // cursor stays on last character.
   1812             C = columns - 1;
   1813           }
   1814         }
   1815 
   1816         boolean mapped = false;
   1817 
   1818         // Mapping if DEC Special is chosen charset
   1819         if (usedcharsets) {
   1820           if (c >= '\u0020' && c <= '\u007f') {
   1821             switch (gx[thisgl]) {
   1822             case '0':
   1823               // Remap SCOANSI line drawing to VT100 line drawing chars
   1824               // for our SCO using customers.
   1825               if (terminalID.equals("scoansi") || terminalID.equals("ansi")) {
   1826                 for (int i = 0; i < scoansi_acs.length(); i += 2) {
   1827                   if (c == scoansi_acs.charAt(i)) {
   1828                     c = scoansi_acs.charAt(i + 1);
   1829                     break;
   1830                   }
   1831                 }
   1832               }
   1833               if (c >= '\u005f' && c <= '\u007e') {
   1834                 c = DECSPECIAL[(short) c - 0x5f];
   1835                 mapped = true;
   1836               }
   1837               break;
   1838             case '<': // 'user preferred' is currently 'ISO Latin-1 suppl
   1839               c = (char) ((c & 0x7f) | 0x80);
   1840               mapped = true;
   1841               break;
   1842             case 'A':
   1843             case 'B': // Latin-1 , ASCII -> fall through
   1844               mapped = true;
   1845               break;
   1846             default:
   1847               debug("Unsupported GL mapping: " + gx[thisgl]);
   1848               break;
   1849             }
   1850           }
   1851           if (!mapped && (c >= '\u0080' && c <= '\u00ff')) {
   1852             switch (gx[gr]) {
   1853             case '0':
   1854               if (c >= '\u00df' && c <= '\u00fe') {
   1855                 c = DECSPECIAL[c - '\u00df'];
   1856                 mapped = true;
   1857               }
   1858               break;
   1859             case '<':
   1860             case 'A':
   1861             case 'B':
   1862               mapped = true;
   1863               break;
   1864             default:
   1865               debug("Unsupported GR mapping: " + gx[gr]);
   1866               break;
   1867             }
   1868           }
   1869         }
   1870         if (!mapped && useibmcharset) {
   1871           c = map_cp850_unicode(c);
   1872         }
   1873 
   1874         /* if(true || (statusmode == 0)) { */
   1875         if (isWide) {
   1876           if (C >= columns - 1) {
   1877             if (wraparound) {
   1878               int bot = rows;
   1879 
   1880               // If we're in the scroll region, check against the bottom margin
   1881               if (R <= getBottomMargin() && R >= getTopMargin()) {
   1882                 bot = getBottomMargin() + 1;
   1883               }
   1884 
   1885               if (R < bot - 1) {
   1886                 R++;
   1887               } else {
   1888                 if (debug > 3) {
   1889                   debug("scrolling due to wrap at " + R);
   1890                 }
   1891                 insertLine(R, 1, SCROLL_UP);
   1892               }
   1893               C = 0;
   1894             } else {
   1895               // cursor stays on last wide character.
   1896               C = columns - 2;
   1897             }
   1898           }
   1899         }
   1900 
   1901         if (insertmode == 1) {
   1902           if (isWide) {
   1903             insertChar(C++, R, c, attributes | FULLWIDTH);
   1904             insertChar(C, R, ' ', attributes | FULLWIDTH);
   1905           } else {
   1906             insertChar(C, R, c, attributes);
   1907           }
   1908         } else {
   1909           if (isWide) {
   1910             putChar(C++, R, c, attributes | FULLWIDTH);
   1911             putChar(C, R, ' ', attributes | FULLWIDTH);
   1912           } else {
   1913             putChar(C, R, c, attributes);
   1914           }
   1915         }
   1916 
   1917         /*
   1918          * } else { if (insertmode==1) { insertChar(C, rows, c, attributes); } else { putChar(C,
   1919          * rows, c, attributes); } }
   1920          */
   1921         C++;
   1922         break;
   1923       }
   1924       } /* switch(c) */
   1925       break;
   1926     case TSTATE_OSC:
   1927       if ((c < 0x20) && (c != ESC)) {// NP - No printing character
   1928         handle_osc(osc);
   1929         term_state = TSTATE_DATA;
   1930         break;
   1931       }
   1932       // but check for vt102 ESC \
   1933       if (c == '\\' && osc.charAt(osc.length() - 1) == ESC) {
   1934         handle_osc(osc);
   1935         term_state = TSTATE_DATA;
   1936         break;
   1937       }
   1938       osc = osc + c;
   1939       break;
   1940     case TSTATE_ESCSPACE:
   1941       term_state = TSTATE_DATA;
   1942       switch (c) {
   1943       case 'F': /* S7C1T, Disable output of 8-bit controls, use 7-bit */
   1944         output8bit = false;
   1945         break;
   1946       case 'G': /* S8C1T, Enable output of 8-bit control codes */
   1947         output8bit = true;
   1948         break;
   1949       default:
   1950         debug("ESC <space> " + c + " unhandled.");
   1951       }
   1952       break;
   1953     case TSTATE_ESC:
   1954       term_state = TSTATE_DATA;
   1955       switch (c) {
   1956       case ' ':
   1957         term_state = TSTATE_ESCSPACE;
   1958         break;
   1959       case '#':
   1960         term_state = TSTATE_ESCSQUARE;
   1961         break;
   1962       case 'c':
   1963         /* Hard terminal reset */
   1964         reset();
   1965         break;
   1966       case '[':
   1967         DCEvar = 0;
   1968         DCEvars[0] = 0;
   1969         DCEvars[1] = 0;
   1970         DCEvars[2] = 0;
   1971         DCEvars[3] = 0;
   1972         term_state = TSTATE_CSI;
   1973         break;
   1974       case ']':
   1975         osc = "";
   1976         term_state = TSTATE_OSC;
   1977         break;
   1978       case 'P':
   1979         dcs = "";
   1980         term_state = TSTATE_DCS;
   1981         break;
   1982       case 'A': /* CUU */
   1983         R--;
   1984         if (R < 0) {
   1985           R = 0;
   1986         }
   1987         break;
   1988       case 'B': /* CUD */
   1989         R++;
   1990         if (R >= rows) {
   1991           R = rows - 1;
   1992         }
   1993         break;
   1994       case 'C':
   1995         C++;
   1996         if (C >= columns) {
   1997           C = columns - 1;
   1998         }
   1999         break;
   2000       case 'I': // RI
   2001         insertLine(R, 1, SCROLL_DOWN);
   2002         break;
   2003       case 'E': /* NEL */
   2004         if (R == getBottomMargin() || R == rows - 1) {
   2005           insertLine(R, 1, SCROLL_UP);
   2006         } else {
   2007           R++;
   2008         }
   2009         C = 0;
   2010         if (debug > 1) {
   2011           debug("ESC E (at " + R + ")");
   2012         }
   2013         break;
   2014       case 'D': /* IND */
   2015         if (R == getBottomMargin() || R == rows - 1) {
   2016           insertLine(R, 1, SCROLL_UP);
   2017         } else {
   2018           R++;
   2019         }
   2020         if (debug > 1) {
   2021           debug("ESC D (at " + R + " )");
   2022         }
   2023         break;
   2024       case 'J': /* erase to end of screen */
   2025         if (R < rows - 1) {
   2026           deleteArea(0, R + 1, columns, rows - R - 1, attributes);
   2027         }
   2028         if (C < columns - 1) {
   2029           deleteArea(C, R, columns - C, 1, attributes);
   2030         }
   2031         break;
   2032       case 'K':
   2033         if (C < columns - 1) {
   2034           deleteArea(C, R, columns - C, 1, attributes);
   2035         }
   2036         break;
   2037       case 'M': // RI
   2038         debug("ESC M : R is " + R + ", tm is " + getTopMargin() + ", bm is " + getBottomMargin());
   2039         if (R > getTopMargin()) { // just go up 1 line.
   2040           R--;
   2041         } else { // scroll down
   2042           insertLine(R, 1, SCROLL_DOWN);
   2043         }
   2044         /* else do nothing ; */
   2045         if (debug > 2) {
   2046           debug("ESC M ");
   2047         }
   2048         break;
   2049       case 'H':
   2050         if (debug > 1) {
   2051           debug("ESC H at " + C);
   2052         }
   2053         /* right border probably ... */
   2054         if (C >= columns) {
   2055           C = columns - 1;
   2056         }
   2057         Tabs[C] = 1;
   2058         break;
   2059       case 'N': // SS2
   2060         onegl = 2;
   2061         break;
   2062       case 'O': // SS3
   2063         onegl = 3;
   2064         break;
   2065       case '=':
   2066         /* application keypad */
   2067         if (debug > 0) {
   2068           debug("ESC =");
   2069         }
   2070         keypadmode = true;
   2071         break;
   2072       case '<': /* vt52 mode off */
   2073         vt52mode = false;
   2074         break;
   2075       case '>': /* normal keypad */
   2076         if (debug > 0) {
   2077           debug("ESC >");
   2078         }
   2079         keypadmode = false;
   2080         break;
   2081       case '7': /* DECSC: save cursor, attributes */
   2082         Sc = C;
   2083         Sr = R;
   2084         Sgl = gl;
   2085         Sgr = gr;
   2086         Sa = attributes;
   2087         Sgx = new char[4];
   2088         for (int i = 0; i < 4; i++) {
   2089           Sgx[i] = gx[i];
   2090         }
   2091         if (debug > 1) {
   2092           debug("ESC 7");
   2093         }
   2094         break;
   2095       case '8': /* DECRC: restore cursor, attributes */
   2096         C = Sc;
   2097         R = Sr;
   2098         gl = Sgl;
   2099         gr = Sgr;
   2100         if (Sgx != null) {
   2101           for (int i = 0; i < 4; i++) {
   2102             gx[i] = Sgx[i];
   2103           }
   2104         }
   2105         attributes = Sa;
   2106         if (debug > 1) {
   2107           debug("ESC 8");
   2108         }
   2109         break;
   2110       case '(': /* Designate G0 Character set (ISO 2022) */
   2111         term_state = TSTATE_SETG0;
   2112         usedcharsets = true;
   2113         break;
   2114       case ')': /* Designate G1 character set (ISO 2022) */
   2115         term_state = TSTATE_SETG1;
   2116         usedcharsets = true;
   2117         break;
   2118       case '*': /* Designate G2 Character set (ISO 2022) */
   2119         term_state = TSTATE_SETG2;
   2120         usedcharsets = true;
   2121         break;
   2122       case '+': /* Designate G3 Character set (ISO 2022) */
   2123         term_state = TSTATE_SETG3;
   2124         usedcharsets = true;
   2125         break;
   2126       case '~': /* Locking Shift 1, right */
   2127         gr = 1;
   2128         usedcharsets = true;
   2129         break;
   2130       case 'n': /* Locking Shift 2 */
   2131         gl = 2;
   2132         usedcharsets = true;
   2133         break;
   2134       case '}': /* Locking Shift 2, right */
   2135         gr = 2;
   2136         usedcharsets = true;
   2137         break;
   2138       case 'o': /* Locking Shift 3 */
   2139         gl = 3;
   2140         usedcharsets = true;
   2141         break;
   2142       case '|': /* Locking Shift 3, right */
   2143         gr = 3;
   2144         usedcharsets = true;
   2145         break;
   2146       case 'Y': /* vt52 cursor address mode , next chars are x,y */
   2147         term_state = TSTATE_VT52Y;
   2148         break;
   2149       case '_':
   2150         term_state = TSTATE_TITLE;
   2151         break;
   2152       case '\\':
   2153         // TODO save title
   2154         term_state = TSTATE_DATA;
   2155         break;
   2156       default:
   2157         debug("ESC unknown letter: " + c + " (" + ((int) c) + ")");
   2158         break;
   2159       }
   2160       break;
   2161     case TSTATE_VT52X:
   2162       C = c - 37;
   2163       if (C < 0) {
   2164         C = 0;
   2165       } else if (C >= width) {
   2166         C = width - 1;
   2167       }
   2168       term_state = TSTATE_VT52Y;
   2169       break;
   2170     case TSTATE_VT52Y:
   2171       R = c - 37;
   2172       if (R < 0) {
   2173         R = 0;
   2174       } else if (R >= height) {
   2175         R = height - 1;
   2176       }
   2177       term_state = TSTATE_DATA;
   2178       break;
   2179     case TSTATE_SETG0:
   2180       if (c != '0' && c != 'A' && c != 'B' && c != '<') {
   2181         debug("ESC ( " + c + ": G0 char set?  (" + ((int) c) + ")");
   2182       } else {
   2183         if (debug > 2) {
   2184           debug("ESC ( : G0 char set  (" + c + " " + ((int) c) + ")");
   2185         }
   2186         gx[0] = c;
   2187       }
   2188       term_state = TSTATE_DATA;
   2189       break;
   2190     case TSTATE_SETG1:
   2191       if (c != '0' && c != 'A' && c != 'B' && c != '<') {
   2192         debug("ESC ) " + c + " (" + ((int) c) + ") :G1 char set?");
   2193       } else {
   2194         if (debug > 2) {
   2195           debug("ESC ) :G1 char set  (" + c + " " + ((int) c) + ")");
   2196         }
   2197         gx[1] = c;
   2198       }
   2199       term_state = TSTATE_DATA;
   2200       break;
   2201     case TSTATE_SETG2:
   2202       if (c != '0' && c != 'A' && c != 'B' && c != '<') {
   2203         debug("ESC*:G2 char set?  (" + ((int) c) + ")");
   2204       } else {
   2205         if (debug > 2) {
   2206           debug("ESC*:G2 char set  (" + c + " " + ((int) c) + ")");
   2207         }
   2208         gx[2] = c;
   2209       }
   2210       term_state = TSTATE_DATA;
   2211       break;
   2212     case TSTATE_SETG3:
   2213       if (c != '0' && c != 'A' && c != 'B' && c != '<') {
   2214         debug("ESC+:G3 char set?  (" + ((int) c) + ")");
   2215       } else {
   2216         if (debug > 2) {
   2217           debug("ESC+:G3 char set  (" + c + " " + ((int) c) + ")");
   2218         }
   2219         gx[3] = c;
   2220       }
   2221       term_state = TSTATE_DATA;
   2222       break;
   2223     case TSTATE_ESCSQUARE:
   2224       switch (c) {
   2225       case '8':
   2226         for (int i = 0; i < columns; i++) {
   2227           for (int j = 0; j < rows; j++) {
   2228             putChar(i, j, 'E', 0);
   2229           }
   2230         }
   2231         break;
   2232       default:
   2233         debug("ESC # " + c + " not supported.");
   2234         break;
   2235       }
   2236       term_state = TSTATE_DATA;
   2237       break;
   2238     case TSTATE_DCS:
   2239       if (c == '\\' && dcs.charAt(dcs.length() - 1) == ESC) {
   2240         handle_dcs(dcs);
   2241         term_state = TSTATE_DATA;
   2242         break;
   2243       }
   2244       dcs = dcs + c;
   2245       break;
   2246 
   2247     case TSTATE_DCEQ:
   2248       term_state = TSTATE_DATA;
   2249       switch (c) {
   2250       case '0':
   2251       case '1':
   2252       case '2':
   2253       case '3':
   2254       case '4':
   2255       case '5':
   2256       case '6':
   2257       case '7':
   2258       case '8':
   2259       case '9':
   2260         DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
   2261         term_state = TSTATE_DCEQ;
   2262         break;
   2263       case ';':
   2264         DCEvar++;
   2265         DCEvars[DCEvar] = 0;
   2266         term_state = TSTATE_DCEQ;
   2267         break;
   2268       case 's': // XTERM_SAVE missing!
   2269         if (true || debug > 1) {
   2270           debug("ESC [ ? " + DCEvars[0] + " s unimplemented!");
   2271         }
   2272         break;
   2273       case 'r': // XTERM_RESTORE
   2274         if (true || debug > 1) {
   2275           debug("ESC [ ? " + DCEvars[0] + " r");
   2276         }
   2277         /* DEC Mode reset */
   2278         for (int i = 0; i <= DCEvar; i++) {
   2279           switch (DCEvars[i]) {
   2280           case 3: /* 80 columns */
   2281             setScreenSize(80, height, true);
   2282             break;
   2283           case 4: /* scrolling mode, smooth */
   2284             break;
   2285           case 5: /* light background */
   2286             break;
   2287           case 6: /* DECOM (Origin Mode) move inside margins. */
   2288             moveoutsidemargins = true;
   2289             break;
   2290           case 7: /* DECAWM: Autowrap Mode */
   2291             wraparound = false;
   2292             break;
   2293           case 12:/* local echo off */
   2294             break;
   2295           case 9: /* X10 mouse */
   2296           case 1000: /* xterm style mouse report on */
   2297           case 1001:
   2298           case 1002:
   2299           case 1003:
   2300             mouserpt = DCEvars[i];
   2301             break;
   2302           default:
   2303             debug("ESC [ ? " + DCEvars[0] + " r, unimplemented!");
   2304           }
   2305         }
   2306         break;
   2307       case 'h': // DECSET
   2308         if (debug > 0) {
   2309           debug("ESC [ ? " + DCEvars[0] + " h");
   2310         }
   2311         /* DEC Mode set */
   2312         for (int i = 0; i <= DCEvar; i++) {
   2313           switch (DCEvars[i]) {
   2314           case 1: /* Application cursor keys */
   2315             KeyUp[0] = "\u001bOA";
   2316             KeyDown[0] = "\u001bOB";
   2317             KeyRight[0] = "\u001bOC";
   2318             KeyLeft[0] = "\u001bOD";
   2319             break;
   2320           case 2: /* DECANM */
   2321             vt52mode = false;
   2322             break;
   2323           case 3: /* 132 columns */
   2324             setScreenSize(132, height, true);
   2325             break;
   2326           case 6: /* DECOM: move inside margins. */
   2327             moveoutsidemargins = false;
   2328             break;
   2329           case 7: /* DECAWM: Autowrap Mode */
   2330             wraparound = true;
   2331             break;
   2332           case 25: /* turn cursor on */
   2333             showCursor(true);
   2334             break;
   2335           case 9: /* X10 mouse */
   2336           case 1000: /* xterm style mouse report on */
   2337           case 1001:
   2338           case 1002:
   2339           case 1003:
   2340             mouserpt = DCEvars[i];
   2341             break;
   2342 
   2343           /* unimplemented stuff, fall through */
   2344           /* 4 - scrolling mode, smooth */
   2345           /* 5 - light background */
   2346           /* 12 - local echo off */
   2347           /* 18 - DECPFF - Printer Form Feed Mode -> On */
   2348           /* 19 - DECPEX - Printer Extent Mode -> Screen */
   2349           default:
   2350             debug("ESC [ ? " + DCEvars[0] + " h, unsupported.");
   2351             break;
   2352           }
   2353         }
   2354         break;
   2355       case 'i': // DEC Printer Control, autoprint, echo screenchars to printer
   2356         // This is different to CSI i!
   2357         // Also: "Autoprint prints a final display line only when the
   2358         // cursor is moved off the line by an autowrap or LF, FF, or
   2359         // VT (otherwise do not print the line)."
   2360         switch (DCEvars[0]) {
   2361         case 1:
   2362           if (debug > 1) {
   2363             debug("CSI ? 1 i : Print line containing cursor");
   2364           }
   2365           break;
   2366         case 4:
   2367           if (debug > 1) {
   2368             debug("CSI ? 4 i : Start passthrough printing");
   2369           }
   2370           break;
   2371         case 5:
   2372           if (debug > 1) {
   2373             debug("CSI ? 4 i : Stop passthrough printing");
   2374           }
   2375           break;
   2376         }
   2377         break;
   2378       case 'l': // DECRST
   2379         /* DEC Mode reset */
   2380         if (debug > 0) {
   2381           debug("ESC [ ? " + DCEvars[0] + " l");
   2382         }
   2383         for (int i = 0; i <= DCEvar; i++) {
   2384           switch (DCEvars[i]) {
   2385           case 1: /* Application cursor keys */
   2386             KeyUp[0] = "\u001b[A";
   2387             KeyDown[0] = "\u001b[B";
   2388             KeyRight[0] = "\u001b[C";
   2389             KeyLeft[0] = "\u001b[D";
   2390             break;
   2391           case 2: /* DECANM */
   2392             vt52mode = true;
   2393             break;
   2394           case 3: /* 80 columns */
   2395             setScreenSize(80, height, true);
   2396             break;
   2397           case 6: /* DECOM: move outside margins. */
   2398             moveoutsidemargins = true;
   2399             break;
   2400           case 7: /* DECAWM: Autowrap Mode OFF */
   2401             wraparound = false;
   2402             break;
   2403           case 25: /* turn cursor off */
   2404             showCursor(false);
   2405             break;
   2406           /* Unimplemented stuff: */
   2407           /* 4 - scrolling mode, jump */
   2408           /* 5 - dark background */
   2409           /* 7 - DECAWM - no wrap around mode */
   2410           /* 12 - local echo on */
   2411           /* 18 - DECPFF - Printer Form Feed Mode -> Off */
   2412           /* 19 - DECPEX - Printer Extent Mode -> Scrolling Region */
   2413           case 9: /* X10 mouse */
   2414           case 1000: /* xterm style mouse report OFF */
   2415           case 1001:
   2416           case 1002:
   2417           case 1003:
   2418             mouserpt = 0;
   2419             break;
   2420           default:
   2421             debug("ESC [ ? " + DCEvars[0] + " l, unsupported.");
   2422             break;
   2423           }
   2424         }
   2425         break;
   2426       case 'n':
   2427         if (debug > 0) {
   2428           debug("ESC [ ? " + DCEvars[0] + " n");
   2429         }
   2430         switch (DCEvars[0]) {
   2431         case 15:
   2432           /* printer? no printer. */
   2433           write((ESC) + "[?13n", false);
   2434           debug("ESC[5n");
   2435           break;
   2436         default:
   2437           debug("ESC [ ? " + DCEvars[0] + " n, unsupported.");
   2438           break;
   2439         }
   2440         break;
   2441       default:
   2442         debug("ESC [ ? " + DCEvars[0] + " " + c + ", unsupported.");
   2443         break;
   2444       }
   2445       break;
   2446     case TSTATE_CSI_EX:
   2447       term_state = TSTATE_DATA;
   2448       switch (c) {
   2449       case ESC:
   2450         term_state = TSTATE_ESC;
   2451         break;
   2452       default:
   2453         debug("Unknown character ESC[! character is " + (int) c);
   2454         break;
   2455       }
   2456       break;
   2457     case TSTATE_CSI_TICKS:
   2458       term_state = TSTATE_DATA;
   2459       switch (c) {
   2460       case 'p':
   2461         debug("Conformance level: " + DCEvars[0] + " (unsupported)," + DCEvars[1]);
   2462         if (DCEvars[0] == 61) {
   2463           output8bit = false;
   2464           break;
   2465         }
   2466         if (DCEvars[1] == 1) {
   2467           output8bit = false;
   2468         } else {
   2469           output8bit = true; /* 0 or 2 */
   2470         }
   2471         break;
   2472       default:
   2473         debug("Unknown ESC [...  \"" + c);
   2474         break;
   2475       }
   2476       break;
   2477     case TSTATE_CSI_EQUAL:
   2478       term_state = TSTATE_DATA;
   2479       switch (c) {
   2480       case '0':
   2481       case '1':
   2482       case '2':
   2483       case '3':
   2484       case '4':
   2485       case '5':
   2486       case '6':
   2487       case '7':
   2488       case '8':
   2489       case '9':
   2490         DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
   2491         term_state = TSTATE_CSI_EQUAL;
   2492         break;
   2493       case ';':
   2494         DCEvar++;
   2495         DCEvars[DCEvar] = 0;
   2496         term_state = TSTATE_CSI_EQUAL;
   2497         break;
   2498 
   2499       case 'F': /* SCO ANSI foreground */
   2500       {
   2501         int newcolor;
   2502 
   2503         debug("ESC [ = " + DCEvars[0] + " F");
   2504 
   2505         attributes &= ~COLOR_FG;
   2506         newcolor = ((DCEvars[0] & 1) << 2) | (DCEvars[0] & 2) | ((DCEvars[0] & 4) >> 2);
   2507         attributes |= (newcolor + 1) << COLOR_FG_SHIFT;
   2508 
   2509         break;
   2510       }
   2511       case 'G': /* SCO ANSI background */
   2512       {
   2513         int newcolor;
   2514 
   2515         debug("ESC [ = " + DCEvars[0] + " G");
   2516 
   2517         attributes &= ~COLOR_BG;
   2518         newcolor = ((DCEvars[0] & 1) << 2) | (DCEvars[0] & 2) | ((DCEvars[0] & 4) >> 2);
   2519         attributes |= (newcolor + 1) << COLOR_BG_SHIFT;
   2520         break;
   2521       }
   2522 
   2523       default:
   2524         debugStr.append("Unknown ESC [ = ");
   2525         for (int i = 0; i <= DCEvar; i++) {
   2526           debugStr.append(DCEvars[i]).append(',');
   2527         }
   2528         debugStr.append(c);
   2529         debug(debugStr.toString());
   2530         debugStr.setLength(0);
   2531         break;
   2532       }
   2533       break;
   2534     case TSTATE_CSI_DOLLAR:
   2535       term_state = TSTATE_DATA;
   2536       switch (c) {
   2537       case '}':
   2538         debug("Active Status Display now " + DCEvars[0]);
   2539         statusmode = DCEvars[0];
   2540         break;
   2541       /*
   2542        * bad documentation? case '-': debug("Set Status Display now "+DCEvars[0]); break;
   2543        */
   2544       case '~':
   2545         debug("Status Line mode now " + DCEvars[0]);
   2546         break;
   2547       default:
   2548         debug("UNKNOWN Status Display code " + c + ", with Pn=" + DCEvars[0]);
   2549         break;
   2550       }
   2551       break;
   2552     case TSTATE_CSI:
   2553       term_state = TSTATE_DATA;
   2554       switch (c) {
   2555       case '"':
   2556         term_state = TSTATE_CSI_TICKS;
   2557         break;
   2558       case '$':
   2559         term_state = TSTATE_CSI_DOLLAR;
   2560         break;
   2561       case '=':
   2562         term_state = TSTATE_CSI_EQUAL;
   2563         break;
   2564       case '!':
   2565         term_state = TSTATE_CSI_EX;
   2566         break;
   2567       case '?':
   2568         DCEvar = 0;
   2569         DCEvars[0] = 0;
   2570         term_state = TSTATE_DCEQ;
   2571         break;
   2572       case '0':
   2573       case '1':
   2574       case '2':
   2575       case '3':
   2576       case '4':
   2577       case '5':
   2578       case '6':
   2579       case '7':
   2580       case '8':
   2581       case '9':
   2582         DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48;
   2583         term_state = TSTATE_CSI;
   2584         break;
   2585       case ';':
   2586         DCEvar++;
   2587         DCEvars[DCEvar] = 0;
   2588         term_state = TSTATE_CSI;
   2589         break;
   2590       case 'c':/* send primary device attributes */
   2591         /* send (ESC[?61c) */
   2592 
   2593         String subcode = "";
   2594         if (terminalID.equals("vt320")) {
   2595           subcode = "63;";
   2596         }
   2597         if (terminalID.equals("vt220")) {
   2598           subcode = "62;";
   2599         }
   2600         if (terminalID.equals("vt100")) {
   2601           subcode = "61;";
   2602         }
   2603         write((ESC) + "[?" + subcode + "1;2c", false);
   2604         if (debug > 1) {
   2605           debug("ESC [ " + DCEvars[0] + " c");
   2606         }
   2607         break;
   2608       case 'q':
   2609         if (debug > 1) {
   2610           debug("ESC [ " + DCEvars[0] + " q");
   2611         }
   2612         break;
   2613       case 'g':
   2614         /* used for tabsets */
   2615         switch (DCEvars[0]) {
   2616         case 3:/* clear them */
   2617           Tabs = new byte[width];
   2618           break;
   2619         case 0:
   2620           Tabs[C] = 0;
   2621           break;
   2622         }
   2623         if (debug > 1) {
   2624           debug("ESC [ " + DCEvars[0] + " g");
   2625         }
   2626         break;
   2627       case 'h':
   2628         switch (DCEvars[0]) {
   2629         case 4:
   2630           insertmode = 1;
   2631           break;
   2632         case 20:
   2633           debug("Setting CRLF to TRUE");
   2634           sendcrlf = true;
   2635           break;
   2636         default:
   2637           debug("unsupported: ESC [ " + DCEvars[0] + " h");
   2638           break;
   2639         }
   2640         if (debug > 1) {
   2641           debug("ESC [ " + DCEvars[0] + " h");
   2642         }
   2643         break;
   2644       case 'i': // Printer Controller mode.
   2645         // "Transparent printing sends all output, except the CSI 4 i
   2646         // termination string, to the printer and not the screen,
   2647         // uses an 8-bit channel if no parity so NUL and DEL will be
   2648         // seen by the printer and by the termination recognizer code,
   2649         // and all translation and character set selections are
   2650         // bypassed."
   2651         switch (DCEvars[0]) {
   2652         case 0:
   2653           if (debug > 1) {
   2654             debug("CSI 0 i:  Print Screen, not implemented.");
   2655           }
   2656           break;
   2657         case 4:
   2658           if (debug > 1) {
   2659             debug("CSI 4 i:  Enable Transparent Printing, not implemented.");
   2660           }
   2661           break;
   2662         case 5:
   2663           if (debug > 1) {
   2664             debug("CSI 4/5 i:  Disable Transparent Printing, not implemented.");
   2665           }
   2666           break;
   2667         default:
   2668           debug("ESC [ " + DCEvars[0] + " i, unimplemented!");
   2669         }
   2670         break;
   2671       case 'l':
   2672         switch (DCEvars[0]) {
   2673         case 4:
   2674           insertmode = 0;
   2675           break;
   2676         case 20:
   2677           debug("Setting CRLF to FALSE");
   2678           sendcrlf = false;
   2679           break;
   2680         default:
   2681           debug("ESC [ " + DCEvars[0] + " l, unimplemented!");
   2682           break;
   2683         }
   2684         break;
   2685       case 'A': // CUU
   2686       {
   2687         int limit;
   2688         /* FIXME: xterm only cares about 0 and topmargin */
   2689         if (R >= getTopMargin()) {
   2690           limit = getTopMargin();
   2691         } else {
   2692           limit = 0;
   2693         }
   2694         if (DCEvars[0] == 0) {
   2695           R--;
   2696         } else {
   2697           R -= DCEvars[0];
   2698         }
   2699         if (R < limit) {
   2700           R = limit;
   2701         }
   2702         if (debug > 1) {
   2703           debug("ESC [ " + DCEvars[0] + " A");
   2704         }
   2705         break;
   2706       }
   2707       case 'B': // CUD
   2708         /* cursor down n (1) times */
   2709       {
   2710         int limit;
   2711         if (R <= getBottomMargin()) {
   2712           limit = getBottomMargin();
   2713         } else {
   2714           limit = rows - 1;
   2715         }
   2716         if (DCEvars[0] == 0) {
   2717           R++;
   2718         } else {
   2719           R += DCEvars[0];
   2720         }
   2721         if (R > limit) {
   2722           R = limit;
   2723         } else {
   2724           if (debug > 2) {
   2725             debug("Not limited.");
   2726           }
   2727         }
   2728         if (debug > 2) {
   2729           debug("to: " + R);
   2730         }
   2731         if (debug > 1) {
   2732           debug("ESC [ " + DCEvars[0] + " B (at C=" + C + ")");
   2733         }
   2734         break;
   2735       }
   2736       case 'C':
   2737         if (DCEvars[0] == 0) {
   2738           DCEvars[0] = 1;
   2739         }
   2740         while (DCEvars[0]-- > 0) {
   2741           C++;
   2742         }
   2743         if (C >= columns) {
   2744           C = columns - 1;
   2745         }
   2746         if (debug > 1) {
   2747           debug("ESC [ " + DCEvars[0] + " C");
   2748         }
   2749         break;
   2750       case 'd': // CVA
   2751         R = DCEvars[0];
   2752         if (R < 0) {
   2753           R = 0;
   2754         } else if (R >= height) {
   2755           R = height - 1;
   2756         }
   2757         if (debug > 1) {
   2758           debug("ESC [ " + DCEvars[0] + " d");
   2759         }
   2760         break;
   2761       case 'D':
   2762         if (DCEvars[0] == 0) {
   2763           DCEvars[0] = 1;
   2764         }
   2765         while (DCEvars[0]-- > 0) {
   2766           C--;
   2767         }
   2768         if (C < 0) {
   2769           C = 0;
   2770         }
   2771         if (debug > 1) {
   2772           debug("ESC [ " + DCEvars[0] + " D");
   2773         }
   2774         break;
   2775       case 'r': // DECSTBM
   2776         if (DCEvar > 0) // Ray: Any argument is optional
   2777         {
   2778           R = DCEvars[1] - 1;
   2779           if (R < 0) {
   2780             R = rows - 1;
   2781           } else if (R >= rows) {
   2782             R = rows - 1;
   2783           }
   2784         } else {
   2785           R = rows - 1;
   2786         }
   2787         int bot = R;
   2788         if (R >= DCEvars[0]) {
   2789           R = DCEvars[0] - 1;
   2790           if (R < 0) {
   2791             R = 0;
   2792           }
   2793         }
   2794         setMargins(R, bot);
   2795         _SetCursor(0, 0);
   2796         if (debug > 1) {
   2797           debug("ESC [" + DCEvars[0] + " ; " + DCEvars[1] + " r");
   2798         }
   2799         break;
   2800       case 'G': /* CUP / cursor absolute column */
   2801         C = DCEvars[0];
   2802         if (C < 0) {
   2803           C = 0;
   2804         } else if (C >= width) {
   2805           C = width - 1;
   2806         }
   2807         if (debug > 1) {
   2808           debug("ESC [ " + DCEvars[0] + " G");
   2809         }
   2810         break;
   2811       case 'H': /* CUP / cursor position */
   2812         /* gets 2 arguments */
   2813         _SetCursor(DCEvars[0] - 1, DCEvars[1] - 1);
   2814         if (debug > 2) {
   2815           debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " H, moveoutsidemargins "
   2816               + moveoutsidemargins);
   2817           debug("	-> R now " + R + ", C now " + C);
   2818         }
   2819         break;
   2820       case 'f': /* move cursor 2 */
   2821         /* gets 2 arguments */
   2822         R = DCEvars[0] - 1;
   2823         C = DCEvars[1] - 1;
   2824         if (C < 0) {
   2825           C = 0;
   2826         } else if (C >= width) {
   2827           C = width - 1;
   2828         }
   2829         if (R < 0) {
   2830           R = 0;
   2831         } else if (R >= height) {
   2832           R = height - 1;
   2833         }
   2834         if (debug > 2) {
   2835           debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " f");
   2836         }
   2837         break;
   2838       case 'S': /* ind aka 'scroll forward' */
   2839         if (DCEvars[0] == 0) {
   2840           insertLine(rows - 1, SCROLL_UP);
   2841         } else {
   2842           insertLine(rows - 1, DCEvars[0], SCROLL_UP);
   2843         }
   2844         break;
   2845       case 'L':
   2846         /* insert n lines */
   2847         if (DCEvars[0] == 0) {
   2848           insertLine(R, SCROLL_DOWN);
   2849         } else {
   2850           insertLine(R, DCEvars[0], SCROLL_DOWN);
   2851         }
   2852         if (debug > 1) {
   2853           debug("ESC [ " + DCEvars[0] + "" + (c) + " (at R " + R + ")");
   2854         }
   2855         break;
   2856       case 'T': /* 'ri' aka scroll backward */
   2857         if (DCEvars[0] == 0) {
   2858           insertLine(0, SCROLL_DOWN);
   2859         } else {
   2860           insertLine(0, DCEvars[0], SCROLL_DOWN);
   2861         }
   2862         break;
   2863       case 'M':
   2864         if (debug > 1) {
   2865           debug("ESC [ " + DCEvars[0] + "" + (c) + " at R=" + R);
   2866         }
   2867         if (DCEvars[0] == 0) {
   2868           deleteLine(R);
   2869         } else {
   2870           for (int i = 0; i < DCEvars[0]; i++) {
   2871             deleteLine(R);
   2872           }
   2873         }
   2874         break;
   2875       case 'K':
   2876         if (debug > 1) {
   2877           debug("ESC [ " + DCEvars[0] + " K");
   2878         }
   2879         /* clear in line */
   2880         switch (DCEvars[0]) {
   2881         case 6: /* 97801 uses ESC[6K for delete to end of line */
   2882         case 0:/* clear to right */
   2883           if (C < columns - 1) {
   2884             deleteArea(C, R, columns - C, 1, attributes);
   2885           }
   2886           break;
   2887         case 1:/* clear to the left, including this */
   2888           if (C > 0) {
   2889             deleteArea(0, R, C + 1, 1, attributes);
   2890           }
   2891           break;
   2892         case 2:/* clear whole line */
   2893           deleteArea(0, R, columns, 1, attributes);
   2894           break;
   2895         }
   2896         break;
   2897       case 'J':
   2898         /* clear below current line */
   2899         switch (DCEvars[0]) {
   2900         case 0:
   2901           if (R < rows - 1) {
   2902             deleteArea(0, R + 1, columns, rows - R - 1, attributes);
   2903           }
   2904           if (C < columns - 1) {
   2905             deleteArea(C, R, columns - C, 1, attributes);
   2906           }
   2907           break;
   2908         case 1:
   2909           if (R > 0) {
   2910             deleteArea(0, 0, columns, R, attributes);
   2911           }
   2912           if (C > 0) {
   2913             deleteArea(0, R, C + 1, 1, attributes);// include up to and including current
   2914           }
   2915           break;
   2916         case 2:
   2917           deleteArea(0, 0, columns, rows, attributes);
   2918           break;
   2919         }
   2920         if (debug > 1) {
   2921           debug("ESC [ " + DCEvars[0] + " J");
   2922         }
   2923         break;
   2924       case '@':
   2925         if (debug > 1) {
   2926           debug("ESC [ " + DCEvars[0] + " @");
   2927         }
   2928         for (int i = 0; i < DCEvars[0]; i++) {
   2929           insertChar(C, R, ' ', attributes);
   2930         }
   2931         break;
   2932       case 'X': {
   2933         int toerase = DCEvars[0];
   2934         if (debug > 1) {
   2935           debug("ESC [ " + DCEvars[0] + " X, C=" + C + ",R=" + R);
   2936         }
   2937         if (toerase == 0) {
   2938           toerase = 1;
   2939         }
   2940         if (toerase + C > columns) {
   2941           toerase = columns - C;
   2942         }
   2943         deleteArea(C, R, toerase, 1, attributes);
   2944         // does not change cursor position
   2945         break;
   2946       }
   2947       case 'P':
   2948         if (debug > 1) {
   2949           debug("ESC [ " + DCEvars[0] + " P, C=" + C + ",R=" + R);
   2950         }
   2951         if (DCEvars[0] == 0) {
   2952           DCEvars[0] = 1;
   2953         }
   2954         for (int i = 0; i < DCEvars[0]; i++) {
   2955           deleteChar(C, R);
   2956         }
   2957         break;
   2958       case 'n':
   2959         switch (DCEvars[0]) {
   2960         case 5: /* malfunction? No malfunction. */
   2961           writeSpecial((ESC) + "[0n");
   2962           if (debug > 1) {
   2963             debug("ESC[5n");
   2964           }
   2965           break;
   2966         case 6:
   2967           // DO NOT offset R and C by 1! (checked against /usr/X11R6/bin/resize
   2968           // FIXME check again.
   2969           // FIXME: but vttest thinks different???
   2970           writeSpecial((ESC) + "[" + R + ";" + C + "R");
   2971           if (debug > 1) {
   2972             debug("ESC[6n");
   2973           }
   2974           break;
   2975         default:
   2976           if (debug > 0) {
   2977             debug("ESC [ " + DCEvars[0] + " n??");
   2978           }
   2979           break;
   2980         }
   2981         break;
   2982       case 's': /* DECSC - save cursor */
   2983         Sc = C;
   2984         Sr = R;
   2985         Sa = attributes;
   2986         if (debug > 3) {
   2987           debug("ESC[s");
   2988         }
   2989         break;
   2990       case 'u': /* DECRC - restore cursor */
   2991         C = Sc;
   2992         R = Sr;
   2993         attributes = Sa;
   2994         if (debug > 3) {
   2995           debug("ESC[u");
   2996         }
   2997         break;
   2998       case 'm': /* attributes as color, bold , blink, */
   2999         if (debug > 3) {
   3000           debug("ESC [ ");
   3001         }
   3002         if (DCEvar == 0 && DCEvars[0] == 0) {
   3003           attributes = 0;
   3004         }
   3005         for (int i = 0; i <= DCEvar; i++) {
   3006           switch (DCEvars[i]) {
   3007           case 0:
   3008             if (DCEvar > 0) {
   3009               if (terminalID.equals("scoansi")) {
   3010                 attributes &= COLOR; /* Keeps color. Strange but true. */
   3011               } else {
   3012                 attributes = 0;
   3013               }
   3014             }
   3015             break;
   3016           case 1:
   3017             attributes |= BOLD;
   3018             attributes &= ~LOW;
   3019             break;
   3020           case 2:
   3021             /* SCO color hack mode */
   3022             if (terminalID.equals("scoansi") && ((DCEvar - i) >= 2)) {
   3023               int ncolor;
   3024               attributes &= ~(COLOR | BOLD);
   3025 
   3026               ncolor = DCEvars[i + 1];
   3027               if ((ncolor & 8) == 8) {
   3028                 attributes |= BOLD;
   3029               }
   3030               ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2);
   3031               attributes |= ((ncolor) + 1) << COLOR_FG_SHIFT;
   3032               ncolor = DCEvars[i + 2];
   3033               ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2);
   3034               attributes |= ((ncolor) + 1) << COLOR_BG_SHIFT;
   3035               i += 2;
   3036             } else {
   3037               attributes |= LOW;
   3038             }
   3039             break;
   3040           case 3: /* italics */
   3041             attributes |= INVERT;
   3042             break;
   3043           case 4:
   3044             attributes |= UNDERLINE;
   3045             break;
   3046           case 7:
   3047             attributes |= INVERT;
   3048             break;
   3049           case 8:
   3050             attributes |= INVISIBLE;
   3051             break;
   3052           case 5: /* blink on */
   3053             break;
   3054           /*
   3055            * 10 - ANSI X3.64-1979, select primary font, don't display control chars, don't set bit 8
   3056            * on output
   3057            */
   3058           case 10:
   3059             gl = 0;
   3060             usedcharsets = true;
   3061             break;
   3062           /*
   3063            * 11 - ANSI X3.64-1979, select second alt. font, display control chars, set bit 8 on
   3064            * output
   3065            */
   3066           case 11: /* SMACS , as */
   3067           case 12:
   3068             gl = 1;
   3069             usedcharsets = true;
   3070             break;
   3071           case 21: /* normal intensity */
   3072             attributes &= ~(LOW | BOLD);
   3073             break;
   3074           case 23: /* italics off */
   3075             attributes &= ~INVERT;
   3076             break;
   3077           case 25: /* blinking off */
   3078             break;
   3079           case 27:
   3080             attributes &= ~INVERT;
   3081             break;
   3082           case 28:
   3083             attributes &= ~INVISIBLE;
   3084             break;
   3085           case 24:
   3086             attributes &= ~UNDERLINE;
   3087             break;
   3088           case 22:
   3089             attributes &= ~BOLD;
   3090             break;
   3091           case 30:
   3092           case 31:
   3093           case 32:
   3094           case 33:
   3095           case 34:
   3096           case 35:
   3097           case 36:
   3098           case 37:
   3099             attributes &= ~COLOR_FG;
   3100             attributes |= ((DCEvars[i] - 30) + 1) << COLOR_FG_SHIFT;
   3101             break;
   3102           case 38:
   3103             if (DCEvars[i + 1] == 5) {
   3104               attributes &= ~COLOR_FG;
   3105               attributes |= ((DCEvars[i + 2]) + 1) << COLOR_FG_SHIFT;
   3106               i += 2;
   3107             }
   3108             break;
   3109           case 39:
   3110             attributes &= ~COLOR_FG;
   3111             break;
   3112           case 40:
   3113           case 41:
   3114           case 42:
   3115           case 43:
   3116           case 44:
   3117           case 45:
   3118           case 46:
   3119           case 47:
   3120             attributes &= ~COLOR_BG;
   3121             attributes |= ((DCEvars[i] - 40) + 1) << COLOR_BG_SHIFT;
   3122             break;
   3123           case 48:
   3124             if (DCEvars[i + 1] == 5) {
   3125               attributes &= ~COLOR_BG;
   3126               attributes |= (DCEvars[i + 2] + 1) << COLOR_BG_SHIFT;
   3127               i += 2;
   3128             }
   3129             break;
   3130           case 49:
   3131             attributes &= ~COLOR_BG;
   3132             break;
   3133           case 90:
   3134           case 91:
   3135           case 92:
   3136           case 93:
   3137           case 94:
   3138           case 95:
   3139           case 96:
   3140           case 97:
   3141             attributes &= ~COLOR_FG;
   3142             attributes |= ((DCEvars[i] - 82) + 1) << COLOR_FG_SHIFT;
   3143             break;
   3144           case 100:
   3145           case 101:
   3146           case 102:
   3147           case 103:
   3148           case 104:
   3149           case 105:
   3150           case 106:
   3151           case 107:
   3152             attributes &= ~COLOR_BG;
   3153             attributes |= ((DCEvars[i] - 92) + 1) << COLOR_BG_SHIFT;
   3154             break;
   3155 
   3156           default:
   3157             debugStr.append("ESC [ ").append(DCEvars[i]).append(" m unknown...");
   3158             debug(debugStr.toString());
   3159             debugStr.setLength(0);
   3160             break;
   3161           }
   3162           if (debug > 3) {
   3163             debugStr.append(DCEvars[i]).append(';');
   3164             debug(debugStr.toString());
   3165             debugStr.setLength(0);
   3166           }
   3167         }
   3168         if (debug > 3) {
   3169           debugStr.append(" (attributes = ").append(attributes).append(")m");
   3170           debug(debugStr.toString());
   3171           debugStr.setLength(0);
   3172         }
   3173         break;
   3174       default:
   3175         debugStr.append("ESC [ unknown letter: ").append(c).append(" (").append((int) c)
   3176             .append(')');
   3177         debug(debugStr.toString());
   3178         debugStr.setLength(0);
   3179         break;
   3180       }
   3181       break;
   3182     case TSTATE_TITLE:
   3183       switch (c) {
   3184       case ESC:
   3185         term_state = TSTATE_ESC;
   3186         break;
   3187       default:
   3188         // TODO save title
   3189         break;
   3190       }
   3191       break;
   3192     default:
   3193       term_state = TSTATE_DATA;
   3194       break;
   3195     }
   3196 
   3197     setCursorPosition(C, R);
   3198   }
   3199 
   3200   /* hard reset the terminal */
   3201   public void reset() {
   3202     gx[0] = 'B';
   3203     gx[1] = 'B';
   3204     gx[2] = 'B';
   3205     gx[3] = 'B';
   3206 
   3207     gl = 0; // default GL to G0
   3208     gr = 2; // default GR to G2
   3209 
   3210     onegl = -1; // Single shift override
   3211 
   3212     /* reset tabs */
   3213     int nw = width;
   3214     if (nw < 132) {
   3215       nw = 132;
   3216     }
   3217     Tabs = new byte[nw];
   3218     for (int i = 0; i < nw; i += 8) {
   3219       Tabs[i] = 1;
   3220     }
   3221 
   3222     deleteArea(0, 0, width, height, attributes);
   3223     setMargins(0, height);
   3224     C = R = 0;
   3225     _SetCursor(0, 0);
   3226 
   3227     if (display != null) {
   3228       display.resetColors();
   3229     }
   3230 
   3231     showCursor(true);
   3232     /* FIXME: */
   3233     term_state = TSTATE_DATA;
   3234   }
   3235 }
   3236