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
26package de.mud.terminal;
27
28import java.util.Properties;
29
30/**
31 * Implementation of a VT terminal emulation plus ANSI compatible.
32 * <P>
33 * <B>Maintainer:</B> Marcus Meißner
34 *
35 * @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $
36 */
37@SuppressWarnings("unused")
38public 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@microsoft.com>
1310    // # Lori Hoerth <lorih@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