diff options
Diffstat (limited to 'app/src/main/java/de/mud')
-rw-r--r-- | app/src/main/java/de/mud/telnet/TelnetProtocolHandler.java | 678 | ||||
-rw-r--r-- | app/src/main/java/de/mud/terminal/Precomposer.java | 1052 | ||||
-rw-r--r-- | app/src/main/java/de/mud/terminal/VDUBuffer.java | 854 | ||||
-rw-r--r-- | app/src/main/java/de/mud/terminal/VDUDisplay.java | 40 | ||||
-rw-r--r-- | app/src/main/java/de/mud/terminal/VDUInput.java | 90 | ||||
-rw-r--r-- | app/src/main/java/de/mud/terminal/vt320.java | 3032 |
6 files changed, 5746 insertions, 0 deletions
diff --git a/app/src/main/java/de/mud/telnet/TelnetProtocolHandler.java b/app/src/main/java/de/mud/telnet/TelnetProtocolHandler.java new file mode 100644 index 0000000..74f08bb --- /dev/null +++ b/app/src/main/java/de/mud/telnet/TelnetProtocolHandler.java @@ -0,0 +1,678 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.telnet; + +import java.io.IOException; +/** + * This is a telnet protocol handler. The handler needs implementations + * for several methods to handle the telnet options and to be able to + * read and write the buffer. + * <P> + * <B>Maintainer:</B> Marcus Meissner + * + * @version $Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $ + * @author Matthias L. Jugel, Marcus Meissner + */ +public abstract class TelnetProtocolHandler { + /** contains the current revision id */ + public final static String ID = "$Id: TelnetProtocolHandler.java 503 2005-10-24 07:34:13Z marcus $"; + + /** debug level */ + private final static int debug = 0; + + /** temporary buffer for data-telnetstuff-data transformation */ + private byte[] tempbuf = new byte[0]; + + /** the data sent on pressing <RETURN> \n */ + private byte[] crlf = new byte[2]; + /** the data sent on pressing <LineFeed> \r */ + private byte[] cr = new byte[2]; + + /** + * Create a new telnet protocol handler. + */ + public TelnetProtocolHandler() { + reset(); + + crlf[0] = 13; crlf[1] = 10; + cr[0] = 13; cr[1] = 0; + } + + /** + * Get the current terminal type for TTYPE telnet option. + * @return the string id of the terminal + */ + protected abstract String getTerminalType(); + + /** + * Get the current window size of the terminal for the + * NAWS telnet option. + * @return the size of the terminal as Dimension + */ + protected abstract int[] getWindowSize(); + + /** + * Set the local echo option of telnet. + * @param echo true for local echo, false for no local echo + */ + protected abstract void setLocalEcho(boolean echo); + + /** + * Generate an EOR (end of record) request. For use by prompt displaying. + */ + protected abstract void notifyEndOfRecord(); + + /** + * Send data to the remote host. + * @param b array of bytes to send + */ + protected abstract void write(byte[] b) throws IOException; + + /** + * Read the charset name from terminal. + */ + protected abstract String getCharsetName(); + + /** + * Send one byte to the remote host. + * @param b the byte to be sent + * @see #write(byte[] b) + */ + private static byte[] one = new byte[1]; + private void write(byte b) throws IOException { + one[0] = b; + write(one); + } + + /** + * Reset the protocol handler. This may be necessary after the + * connection was closed or some other problem occured. + */ + public void reset() { + neg_state = 0; + receivedDX = new byte[256]; + sentDX = new byte[256]; + receivedWX = new byte[256]; + sentWX = new byte[256]; + } + + // =================================================================== + // the actual negotiation handling for the telnet protocol follows: + // =================================================================== + + /** state variable for telnet negotiation reader */ + private byte neg_state = 0; + + /** constants for the negotiation state */ + private final static byte STATE_DATA = 0; + private final static byte STATE_IAC = 1; + private final static byte STATE_IACSB = 2; + private final static byte STATE_IACWILL = 3; + private final static byte STATE_IACDO = 4; + private final static byte STATE_IACWONT = 5; + private final static byte STATE_IACDONT = 6; + private final static byte STATE_IACSBIAC = 7; + private final static byte STATE_IACSBDATA = 8; + private final static byte STATE_IACSBDATAIAC = 9; + + /** What IAC SB <xx> we are handling right now */ + private byte current_sb; + + /** current SB negotiation buffer */ + private byte[] sbbuf; + + /** IAC - init sequence for telnet negotiation. */ + private final static byte IAC = (byte)255; + /** [IAC] End Of Record */ + private final static byte EOR = (byte)239; + /** [IAC] WILL */ + private final static byte WILL = (byte)251; + /** [IAC] WONT */ + private final static byte WONT = (byte)252; + /** [IAC] DO */ + private final static byte DO = (byte)253; + /** [IAC] DONT */ + private final static byte DONT = (byte)254; + /** [IAC] Sub Begin */ + private final static byte SB = (byte)250; + /** [IAC] Sub End */ + private final static byte SE = (byte)240; + /** Telnet option: binary mode */ + private final static byte TELOPT_BINARY= (byte)0; /* binary mode */ + /** Telnet option: echo text */ + private final static byte TELOPT_ECHO = (byte)1; /* echo on/off */ + /** Telnet option: sga */ + private final static byte TELOPT_SGA = (byte)3; /* supress go ahead */ + /** Telnet option: End Of Record */ + private final static byte TELOPT_EOR = (byte)25; /* end of record */ + /** Telnet option: Negotiate About Window Size */ + private final static byte TELOPT_NAWS = (byte)31; /* NA-WindowSize*/ + /** Telnet option: Terminal Type */ + private final static byte TELOPT_TTYPE = (byte)24; /* terminal type */ + /** Telnet option: CHARSET */ + private final static byte TELOPT_CHARSET= (byte)42; /* charset */ + + private final static byte[] IACWILL = { IAC, WILL }; + private final static byte[] IACWONT = { IAC, WONT }; + private final static byte[] IACDO = { IAC, DO }; + private final static byte[] IACDONT = { IAC, DONT }; + private final static byte[] IACSB = { IAC, SB }; + private final static byte[] IACSE = { IAC, SE }; + + private final static byte CHARSET_ACCEPTED = (byte)2; + private final static byte CHARSET_REJECTED = (byte)3; + + /** Telnet option qualifier 'IS' */ + private final static byte TELQUAL_IS = (byte)0; + /** Telnet option qualifier 'SEND' */ + private final static byte TELQUAL_SEND = (byte)1; + + /** What IAC DO(NT) request do we have received already ? */ + private byte[] receivedDX; + /** What IAC WILL/WONT request do we have received already ? */ + private byte[] receivedWX; + /** What IAC DO/DONT request do we have sent already ? */ + private byte[] sentDX; + /** What IAC WILL/WONT request do we have sent already ? */ + private byte[] sentWX; + + /** + * Send a Telnet Escape character (IAC <code>) + */ + public void sendTelnetControl(byte code) + throws IOException { + byte[] b = new byte[2]; + + b[0] = IAC; + b[1] = code; + write(b); + } + + /** + * Send the new Window Size (via NAWS) + */ + public void setWindowSize(int columns,int rows) + throws IOException { + if(debug > 2) System.err.println("sending NAWS"); + + if (receivedDX[TELOPT_NAWS] != DO) { + System.err.println("not allowed to send NAWS? (DONT NAWS)"); + return; + } + write(IAC);write(SB);write(TELOPT_NAWS); + write((byte) (columns >> 8)); + write((byte) (columns & 0xff)); + write((byte) (rows >> 8)); + write((byte) (rows & 0xff)); + write(IAC);write(SE); + } + + + /** + * Handle an incoming IAC SB <type> <bytes> IAC SE + * @param type type of SB + * @param sbata byte array as <bytes> + */ + private void handle_sb(byte type, byte[] sbdata) + throws IOException { + if(debug > 1) + System.err.println("TelnetIO.handle_sb("+type+")"); + switch (type) { + case TELOPT_TTYPE: + if (sbdata.length>0 && sbdata[0]==TELQUAL_SEND) { + write(IACSB);write(TELOPT_TTYPE);write(TELQUAL_IS); + /* FIXME: need more logic here if we use + * more than one terminal type + */ + String ttype = getTerminalType(); + if(ttype == null) ttype = "dumb"; + write(ttype.getBytes()); + write(IACSE); + } + break; + case TELOPT_CHARSET: + System.out.println("Got SB CHARSET"); + + String charsetStr = new String(sbdata, "US-ASCII"); + if (charsetStr.startsWith("TTABLE ")) { + charsetStr = charsetStr.substring(7); + } + String[] charsets = charsetStr.split(charsetStr.substring(0,0)); + String myCharset = getCharsetName(); + for (String charset : charsets) { + if (charset.equals(myCharset)) { + write(IACSB);write(TELOPT_CHARSET);write(CHARSET_ACCEPTED); + write(charset.getBytes()); + write(IACSE); + System.out.println("Sent our charset!"); + return; + } + } + write(IACSB);write(TELOPT_CHARSET);write(CHARSET_REJECTED); + write(IACSE); + break; + } + } + + /** + * Do not send any notifications at startup. We do not know, + * whether the remote client understands telnet protocol handling, + * so we are silent. + * (This used to send IAC WILL SGA, but this is false for a compliant + * client.) + */ + public void startup() throws IOException { + } + /** + * Transpose special telnet codes like 0xff or newlines to values + * that are compliant to the protocol. This method will also send + * the buffer immediately after transposing the data. + * @param buf the data buffer to be sent + */ + public void transpose(byte[] buf) throws IOException { + int i; + + byte[] nbuf,xbuf; + int nbufptr=0; + nbuf = new byte[buf.length*2]; // FIXME: buffer overflows possible + + for (i = 0; i < buf.length ; i++) { + switch (buf[i]) { + // Escape IAC twice in stream ... to be telnet protocol compliant + // this is there in binary and non-binary mode. + case IAC: + nbuf[nbufptr++]=IAC; + nbuf[nbufptr++]=IAC; + break; + // We need to heed RFC 854. LF (\n) is 10, CR (\r) is 13 + // we assume that the Terminal sends \n for lf+cr and \r for just cr + // linefeed+carriage return is CR LF */ + case 10: // \n + if (receivedDX[TELOPT_BINARY + 128 ] != DO) { + while (nbuf.length - nbufptr < crlf.length) { + xbuf = new byte[nbuf.length*2]; + System.arraycopy(nbuf,0,xbuf,0,nbufptr); + nbuf = xbuf; + } + for (int j=0;j<crlf.length;j++) + nbuf[nbufptr++]=crlf[j]; + break; + } else { + // copy verbatim in binary mode. + nbuf[nbufptr++]=buf[i]; + } + break; + // carriage return is CR NUL */ + case 13: // \r + if (receivedDX[TELOPT_BINARY + 128 ] != DO) { + while (nbuf.length - nbufptr < cr.length) { + xbuf = new byte[nbuf.length*2]; + System.arraycopy(nbuf,0,xbuf,0,nbufptr); + nbuf = xbuf; + } + for (int j=0;j<cr.length;j++) + nbuf[nbufptr++]=cr[j]; + } else { + // copy verbatim in binary mode. + nbuf[nbufptr++]=buf[i]; + } + break; + // all other characters are just copied + default: + nbuf[nbufptr++]=buf[i]; + break; + } + } + xbuf = new byte[nbufptr]; + System.arraycopy(nbuf,0,xbuf,0,nbufptr); + write(xbuf); + } + + public void setCRLF(String xcrlf) { crlf = xcrlf.getBytes(); } + public void setCR(String xcr) { cr = xcr.getBytes(); } + + /** + * Handle telnet protocol negotiation. The buffer will be parsed + * and necessary actions are taken according to the telnet protocol. + * See <A HREF="RFC-Telnet-URL">RFC-Telnet</A> + * @param nbuf the byte buffer put out after negotiation + * @return number of bytes processed, 0 for none, and -1 for end of buffer. + */ + public int negotiate(byte nbuf[], int offset) + throws IOException + { + int count = tempbuf.length; + byte[] buf = tempbuf; + byte sendbuf[] = new byte[3]; + byte b,reply; + int boffset = 0, noffset = offset; + boolean dobreak = false; + + if (count == 0) // buffer is empty. + return -1; + + while(!dobreak && (boffset < count) && (noffset < nbuf.length)) { + b=buf[boffset++]; + // of course, byte is a signed entity (-128 -> 127) + // but apparently the SGI Netscape 3.0 doesn't seem + // to care and provides happily values up to 255 + if (b>=128) + b=(byte)(b-256); + if(debug > 2) { + Byte B = new Byte(b); + System.err.print("byte: " + B.intValue()+ " "); + } + switch (neg_state) { + case STATE_DATA: + if (b==IAC) { + neg_state = STATE_IAC; + dobreak = true; // leave the loop so we can sync. + } else + nbuf[noffset++]=b; + break; + case STATE_IAC: + switch (b) { + case IAC: + if(debug > 2) System.err.print("IAC "); + neg_state = STATE_DATA; + nbuf[noffset++]=IAC; + break; + case WILL: + if(debug > 2) System.err.print("WILL "); + neg_state = STATE_IACWILL; + break; + case WONT: + if(debug > 2) System.err.print("WONT "); + neg_state = STATE_IACWONT; + break; + case DONT: + if(debug > 2) System.err.print("DONT "); + neg_state = STATE_IACDONT; + break; + case DO: + if(debug > 2) System.err.print("DO "); + neg_state = STATE_IACDO; + break; + case EOR: + if(debug > 1) System.err.print("EOR "); + notifyEndOfRecord(); + dobreak = true; // leave the loop so we can sync. + neg_state = STATE_DATA; + break; + case SB: + if(debug > 2) System.err.print("SB "); + neg_state = STATE_IACSB; + break; + default: + if(debug > 2) System.err.print("<UNKNOWN "+b+" > "); + neg_state = STATE_DATA; + break; + } + break; + case STATE_IACWILL: + switch(b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = DO; + setLocalEcho(false); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = DO; + break; + case TELOPT_EOR: + if(debug > 2) System.err.println("EOR"); + reply = DO; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = DO; + break; + default: + if(debug > 2) System.err.println("<UNKNOWN,"+b+">"); + reply = DONT; + break; + } + if(debug > 1) System.err.println("<"+b+", WILL ="+WILL+">"); + if (reply != sentDX[b+128] || WILL != receivedWX[b+128]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentDX[b+128] = reply; + receivedWX[b+128] = WILL; + } + neg_state = STATE_DATA; + break; + case STATE_IACWONT: + switch(b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + setLocalEcho(true); + reply = DONT; + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = DONT; + break; + case TELOPT_EOR: + if(debug > 2) System.err.println("EOR"); + reply = DONT; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = DONT; + break; + default: + if(debug > 2) System.err.println("<UNKNOWN,"+b+">"); + reply = DONT; + break; + } + if(reply != sentDX[b+128] || WONT != receivedWX[b+128]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentDX[b+128] = reply; + receivedWX[b+128] = WILL; + } + neg_state = STATE_DATA; + break; + case STATE_IACDO: + switch (b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = WILL; + setLocalEcho(true); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = WILL; + break; + case TELOPT_TTYPE: + if(debug > 2) System.err.println("TTYPE"); + reply = WILL; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = WILL; + break; + case TELOPT_NAWS: + if(debug > 2) System.err.println("NAWS"); + int[] size = getWindowSize(); + receivedDX[b] = DO; + if(size == null) { + // this shouldn't happen + write(IAC); + write(WONT); + write(TELOPT_NAWS); + reply = WONT; + sentWX[b] = WONT; + break; + } + reply = WILL; + sentWX[b] = WILL; + sendbuf[0]=IAC; + sendbuf[1]=WILL; + sendbuf[2]=TELOPT_NAWS; + write(sendbuf); + write(IAC);write(SB);write(TELOPT_NAWS); + write((byte) (size[0] >> 8)); + write((byte) (size[0] & 0xff)); + write((byte) (size[1] >> 8)); + write((byte) (size[1] & 0xff)); + write(IAC);write(SE); + break; + default: + if(debug > 2) System.err.println("<UNKNOWN,"+b+">"); + reply = WONT; + break; + } + if(reply != sentWX[128+b] || DO != receivedDX[128+b]) { + sendbuf[0]=IAC; + sendbuf[1]=reply; + sendbuf[2]=b; + write(sendbuf); + sentWX[b+128] = reply; + receivedDX[b+128] = DO; + } + neg_state = STATE_DATA; + break; + case STATE_IACDONT: + switch (b) { + case TELOPT_ECHO: + if(debug > 2) System.err.println("ECHO"); + reply = WONT; + setLocalEcho(false); + break; + case TELOPT_SGA: + if(debug > 2) System.err.println("SGA"); + reply = WONT; + break; + case TELOPT_NAWS: + if(debug > 2) System.err.println("NAWS"); + reply = WONT; + break; + case TELOPT_BINARY: + if(debug > 2) System.err.println("BINARY"); + reply = WONT; + break; + default: + if(debug > 2) System.err.println("<UNKNOWN,"+b+">"); + reply = WONT; + break; + } + if(reply != sentWX[b+128] || DONT != receivedDX[b+128]) { + write(IAC);write(reply);write(b); + sentWX[b+128] = reply; + receivedDX[b+128] = DONT; + } + neg_state = STATE_DATA; + break; + case STATE_IACSBIAC: + if(debug > 2) System.err.println(""+b+" "); + if (b == IAC) { + sbbuf = new byte[0]; + current_sb = b; + neg_state = STATE_IACSBDATA; + } else { + System.err.println("(bad) "+b+" "); + neg_state = STATE_DATA; + } + break; + case STATE_IACSB: + if(debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBIAC; + break; + default: + current_sb = b; + sbbuf = new byte[0]; + neg_state = STATE_IACSBDATA; + break; + } + break; + case STATE_IACSBDATA: + if (debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBDATAIAC; + break; + default: + byte[] xsb = new byte[sbbuf.length+1]; + System.arraycopy(sbbuf,0,xsb,0,sbbuf.length); + sbbuf = xsb; + sbbuf[sbbuf.length-1] = b; + break; + } + break; + case STATE_IACSBDATAIAC: + if (debug > 2) System.err.println(""+b+" "); + switch (b) { + case IAC: + neg_state = STATE_IACSBDATA; + byte[] xsb = new byte[sbbuf.length+1]; + System.arraycopy(sbbuf,0,xsb,0,sbbuf.length); + sbbuf = xsb; + sbbuf[sbbuf.length-1] = IAC; + break; + case SE: + handle_sb(current_sb,sbbuf); + current_sb = 0; + neg_state = STATE_DATA; + break; + case SB: + handle_sb(current_sb,sbbuf); + neg_state = STATE_IACSB; + break; + default: + neg_state = STATE_DATA; + break; + } + break; + default: + if (debug > 1) + System.err.println("This should not happen: "+neg_state+" "); + neg_state = STATE_DATA; + break; + } + } + // shrink tempbuf to new processed size. + byte[] xb = new byte[count-boffset]; + System.arraycopy(tempbuf,boffset,xb,0,count-boffset); + tempbuf = xb; + return noffset - offset; + } + + public void inputfeed(byte[] b, int offset, int len) { + byte[] xb = new byte[tempbuf.length+len]; + + System.arraycopy(tempbuf,0,xb,0,tempbuf.length); + System.arraycopy(b,offset,xb,tempbuf.length,len); + tempbuf = xb; + } +} diff --git a/app/src/main/java/de/mud/terminal/Precomposer.java b/app/src/main/java/de/mud/terminal/Precomposer.java new file mode 100644 index 0000000..edad64c --- /dev/null +++ b/app/src/main/java/de/mud/terminal/Precomposer.java @@ -0,0 +1,1052 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package de.mud.terminal; + +/** + * @author Kenny Root + * This data was taken from xterm's precompose.c + */ +public class Precomposer { + public final static char precompositions[][] = { + { 0x226E, 0x003C, 0x0338}, + { 0x2260, 0x003D, 0x0338}, + { 0x226F, 0x003E, 0x0338}, + { 0x00C0, 0x0041, 0x0300}, + { 0x00C1, 0x0041, 0x0301}, + { 0x00C2, 0x0041, 0x0302}, + { 0x00C3, 0x0041, 0x0303}, + { 0x0100, 0x0041, 0x0304}, + { 0x0102, 0x0041, 0x0306}, + { 0x0226, 0x0041, 0x0307}, + { 0x00C4, 0x0041, 0x0308}, + { 0x1EA2, 0x0041, 0x0309}, + { 0x00C5, 0x0041, 0x030A}, + { 0x01CD, 0x0041, 0x030C}, + { 0x0200, 0x0041, 0x030F}, + { 0x0202, 0x0041, 0x0311}, + { 0x1EA0, 0x0041, 0x0323}, + { 0x1E00, 0x0041, 0x0325}, + { 0x0104, 0x0041, 0x0328}, + { 0x1E02, 0x0042, 0x0307}, + { 0x1E04, 0x0042, 0x0323}, + { 0x1E06, 0x0042, 0x0331}, + { 0x0106, 0x0043, 0x0301}, + { 0x0108, 0x0043, 0x0302}, + { 0x010A, 0x0043, 0x0307}, + { 0x010C, 0x0043, 0x030C}, + { 0x00C7, 0x0043, 0x0327}, + { 0x1E0A, 0x0044, 0x0307}, + { 0x010E, 0x0044, 0x030C}, + { 0x1E0C, 0x0044, 0x0323}, + { 0x1E10, 0x0044, 0x0327}, + { 0x1E12, 0x0044, 0x032D}, + { 0x1E0E, 0x0044, 0x0331}, + { 0x00C8, 0x0045, 0x0300}, + { 0x00C9, 0x0045, 0x0301}, + { 0x00CA, 0x0045, 0x0302}, + { 0x1EBC, 0x0045, 0x0303}, + { 0x0112, 0x0045, 0x0304}, + { 0x0114, 0x0045, 0x0306}, + { 0x0116, 0x0045, 0x0307}, + { 0x00CB, 0x0045, 0x0308}, + { 0x1EBA, 0x0045, 0x0309}, + { 0x011A, 0x0045, 0x030C}, + { 0x0204, 0x0045, 0x030F}, + { 0x0206, 0x0045, 0x0311}, + { 0x1EB8, 0x0045, 0x0323}, + { 0x0228, 0x0045, 0x0327}, + { 0x0118, 0x0045, 0x0328}, + { 0x1E18, 0x0045, 0x032D}, + { 0x1E1A, 0x0045, 0x0330}, + { 0x1E1E, 0x0046, 0x0307}, + { 0x01F4, 0x0047, 0x0301}, + { 0x011C, 0x0047, 0x0302}, + { 0x1E20, 0x0047, 0x0304}, + { 0x011E, 0x0047, 0x0306}, + { 0x0120, 0x0047, 0x0307}, + { 0x01E6, 0x0047, 0x030C}, + { 0x0122, 0x0047, 0x0327}, + { 0x0124, 0x0048, 0x0302}, + { 0x1E22, 0x0048, 0x0307}, + { 0x1E26, 0x0048, 0x0308}, + { 0x021E, 0x0048, 0x030C}, + { 0x1E24, 0x0048, 0x0323}, + { 0x1E28, 0x0048, 0x0327}, + { 0x1E2A, 0x0048, 0x032E}, + { 0x00CC, 0x0049, 0x0300}, + { 0x00CD, 0x0049, 0x0301}, + { 0x00CE, 0x0049, 0x0302}, + { 0x0128, 0x0049, 0x0303}, + { 0x012A, 0x0049, 0x0304}, + { 0x012C, 0x0049, 0x0306}, + { 0x0130, 0x0049, 0x0307}, + { 0x00CF, 0x0049, 0x0308}, + { 0x1EC8, 0x0049, 0x0309}, + { 0x01CF, 0x0049, 0x030C}, + { 0x0208, 0x0049, 0x030F}, + { 0x020A, 0x0049, 0x0311}, + { 0x1ECA, 0x0049, 0x0323}, + { 0x012E, 0x0049, 0x0328}, + { 0x1E2C, 0x0049, 0x0330}, + { 0x0134, 0x004A, 0x0302}, + { 0x1E30, 0x004B, 0x0301}, + { 0x01E8, 0x004B, 0x030C}, + { 0x1E32, 0x004B, 0x0323}, + { 0x0136, 0x004B, 0x0327}, + { 0x1E34, 0x004B, 0x0331}, + { 0x0139, 0x004C, 0x0301}, + { 0x013D, 0x004C, 0x030C}, + { 0x1E36, 0x004C, 0x0323}, + { 0x013B, 0x004C, 0x0327}, + { 0x1E3C, 0x004C, 0x032D}, + { 0x1E3A, 0x004C, 0x0331}, + { 0x1E3E, 0x004D, 0x0301}, + { 0x1E40, 0x004D, 0x0307}, + { 0x1E42, 0x004D, 0x0323}, + { 0x01F8, 0x004E, 0x0300}, + { 0x0143, 0x004E, 0x0301}, + { 0x00D1, 0x004E, 0x0303}, + { 0x1E44, 0x004E, 0x0307}, + { 0x0147, 0x004E, 0x030C}, + { 0x1E46, 0x004E, 0x0323}, + { 0x0145, 0x004E, 0x0327}, + { 0x1E4A, 0x004E, 0x032D}, + { 0x1E48, 0x004E, 0x0331}, + { 0x00D2, 0x004F, 0x0300}, + { 0x00D3, 0x004F, 0x0301}, + { 0x00D4, 0x004F, 0x0302}, + { 0x00D5, 0x004F, 0x0303}, + { 0x014C, 0x004F, 0x0304}, + { 0x014E, 0x004F, 0x0306}, + { 0x022E, 0x004F, 0x0307}, + { 0x00D6, 0x004F, 0x0308}, + { 0x1ECE, 0x004F, 0x0309}, + { 0x0150, 0x004F, 0x030B}, + { 0x01D1, 0x004F, 0x030C}, + { 0x020C, 0x004F, 0x030F}, + { 0x020E, 0x004F, 0x0311}, + { 0x01A0, 0x004F, 0x031B}, + { 0x1ECC, 0x004F, 0x0323}, + { 0x01EA, 0x004F, 0x0328}, + { 0x1E54, 0x0050, 0x0301}, + { 0x1E56, 0x0050, 0x0307}, + { 0x0154, 0x0052, 0x0301}, + { 0x1E58, 0x0052, 0x0307}, + { 0x0158, 0x0052, 0x030C}, + { 0x0210, 0x0052, 0x030F}, + { 0x0212, 0x0052, 0x0311}, + { 0x1E5A, 0x0052, 0x0323}, + { 0x0156, 0x0052, 0x0327}, + { 0x1E5E, 0x0052, 0x0331}, + { 0x015A, 0x0053, 0x0301}, + { 0x015C, 0x0053, 0x0302}, + { 0x1E60, 0x0053, 0x0307}, + { 0x0160, 0x0053, 0x030C}, + { 0x1E62, 0x0053, 0x0323}, + { 0x0218, 0x0053, 0x0326}, + { 0x015E, 0x0053, 0x0327}, + { 0x1E6A, 0x0054, 0x0307}, + { 0x0164, 0x0054, 0x030C}, + { 0x1E6C, 0x0054, 0x0323}, + { 0x021A, 0x0054, 0x0326}, + { 0x0162, 0x0054, 0x0327}, + { 0x1E70, 0x0054, 0x032D}, + { 0x1E6E, 0x0054, 0x0331}, + { 0x00D9, 0x0055, 0x0300}, + { 0x00DA, 0x0055, 0x0301}, + { 0x00DB, 0x0055, 0x0302}, + { 0x0168, 0x0055, 0x0303}, + { 0x016A, 0x0055, 0x0304}, + { 0x016C, 0x0055, 0x0306}, + { 0x00DC, 0x0055, 0x0308}, + { 0x1EE6, 0x0055, 0x0309}, + { 0x016E, 0x0055, 0x030A}, + { 0x0170, 0x0055, 0x030B}, + { 0x01D3, 0x0055, 0x030C}, + { 0x0214, 0x0055, 0x030F}, + { 0x0216, 0x0055, 0x0311}, + { 0x01AF, 0x0055, 0x031B}, + { 0x1EE4, 0x0055, 0x0323}, + { 0x1E72, 0x0055, 0x0324}, + { 0x0172, 0x0055, 0x0328}, + { 0x1E76, 0x0055, 0x032D}, + { 0x1E74, 0x0055, 0x0330}, + { 0x1E7C, 0x0056, 0x0303}, + { 0x1E7E, 0x0056, 0x0323}, + { 0x1E80, 0x0057, 0x0300}, + { 0x1E82, 0x0057, 0x0301}, + { 0x0174, 0x0057, 0x0302}, + { 0x1E86, 0x0057, 0x0307}, + { 0x1E84, 0x0057, 0x0308}, + { 0x1E88, 0x0057, 0x0323}, + { 0x1E8A, 0x0058, 0x0307}, + { 0x1E8C, 0x0058, 0x0308}, + { 0x1EF2, 0x0059, 0x0300}, + { 0x00DD, 0x0059, 0x0301}, + { 0x0176, 0x0059, 0x0302}, + { 0x1EF8, 0x0059, 0x0303}, + { 0x0232, 0x0059, 0x0304}, + { 0x1E8E, 0x0059, 0x0307}, + { 0x0178, 0x0059, 0x0308}, + { 0x1EF6, 0x0059, 0x0309}, + { 0x1EF4, 0x0059, 0x0323}, + { 0x0179, 0x005A, 0x0301}, + { 0x1E90, 0x005A, 0x0302}, + { 0x017B, 0x005A, 0x0307}, + { 0x017D, 0x005A, 0x030C}, + { 0x1E92, 0x005A, 0x0323}, + { 0x1E94, 0x005A, 0x0331}, + { 0x00E0, 0x0061, 0x0300}, + { 0x00E1, 0x0061, 0x0301}, + { 0x00E2, 0x0061, 0x0302}, + { 0x00E3, 0x0061, 0x0303}, + { 0x0101, 0x0061, 0x0304}, + { 0x0103, 0x0061, 0x0306}, + { 0x0227, 0x0061, 0x0307}, + { 0x00E4, 0x0061, 0x0308}, + { 0x1EA3, 0x0061, 0x0309}, + { 0x00E5, 0x0061, 0x030A}, + { 0x01CE, 0x0061, 0x030C}, + { 0x0201, 0x0061, 0x030F}, + { 0x0203, 0x0061, 0x0311}, + { 0x1EA1, 0x0061, 0x0323}, + { 0x1E01, 0x0061, 0x0325}, + { 0x0105, 0x0061, 0x0328}, + { 0x1E03, 0x0062, 0x0307}, + { 0x1E05, 0x0062, 0x0323}, + { 0x1E07, 0x0062, 0x0331}, + { 0x0107, 0x0063, 0x0301}, + { 0x0109, 0x0063, 0x0302}, + { 0x010B, 0x0063, 0x0307}, + { 0x010D, 0x0063, 0x030C}, + { 0x00E7, 0x0063, 0x0327}, + { 0x1E0B, 0x0064, 0x0307}, + { 0x010F, 0x0064, 0x030C}, + { 0x1E0D, 0x0064, 0x0323}, + { 0x1E11, 0x0064, 0x0327}, + { 0x1E13, 0x0064, 0x032D}, + { 0x1E0F, 0x0064, 0x0331}, + { 0x00E8, 0x0065, 0x0300}, + { 0x00E9, 0x0065, 0x0301}, + { 0x00EA, 0x0065, 0x0302}, + { 0x1EBD, 0x0065, 0x0303}, + { 0x0113, 0x0065, 0x0304}, + { 0x0115, 0x0065, 0x0306}, + { 0x0117, 0x0065, 0x0307}, + { 0x00EB, 0x0065, 0x0308}, + { 0x1EBB, 0x0065, 0x0309}, + { 0x011B, 0x0065, 0x030C}, + { 0x0205, 0x0065, 0x030F}, + { 0x0207, 0x0065, 0x0311}, + { 0x1EB9, 0x0065, 0x0323}, + { 0x0229, 0x0065, 0x0327}, + { 0x0119, 0x0065, 0x0328}, + { 0x1E19, 0x0065, 0x032D}, + { 0x1E1B, 0x0065, 0x0330}, + { 0x1E1F, 0x0066, 0x0307}, + { 0x01F5, 0x0067, 0x0301}, + { 0x011D, 0x0067, 0x0302}, + { 0x1E21, 0x0067, 0x0304}, + { 0x011F, 0x0067, 0x0306}, + { 0x0121, 0x0067, 0x0307}, + { 0x01E7, 0x0067, 0x030C}, + { 0x0123, 0x0067, 0x0327}, + { 0x0125, 0x0068, 0x0302}, + { 0x1E23, 0x0068, 0x0307}, + { 0x1E27, 0x0068, 0x0308}, + { 0x021F, 0x0068, 0x030C}, + { 0x1E25, 0x0068, 0x0323}, + { 0x1E29, 0x0068, 0x0327}, + { 0x1E2B, 0x0068, 0x032E}, + { 0x1E96, 0x0068, 0x0331}, + { 0x00EC, 0x0069, 0x0300}, + { 0x00ED, 0x0069, 0x0301}, + { 0x00EE, 0x0069, 0x0302}, + { 0x0129, 0x0069, 0x0303}, + { 0x012B, 0x0069, 0x0304}, + { 0x012D, 0x0069, 0x0306}, + { 0x00EF, 0x0069, 0x0308}, + { 0x1EC9, 0x0069, 0x0309}, + { 0x01D0, 0x0069, 0x030C}, + { 0x0209, 0x0069, 0x030F}, + { 0x020B, 0x0069, 0x0311}, + { 0x1ECB, 0x0069, 0x0323}, + { 0x012F, 0x0069, 0x0328}, + { 0x1E2D, 0x0069, 0x0330}, + { 0x0135, 0x006A, 0x0302}, + { 0x01F0, 0x006A, 0x030C}, + { 0x1E31, 0x006B, 0x0301}, + { 0x01E9, 0x006B, 0x030C}, + { 0x1E33, 0x006B, 0x0323}, + { 0x0137, 0x006B, 0x0327}, + { 0x1E35, 0x006B, 0x0331}, + { 0x013A, 0x006C, 0x0301}, + { 0x013E, 0x006C, 0x030C}, + { 0x1E37, 0x006C, 0x0323}, + { 0x013C, 0x006C, 0x0327}, + { 0x1E3D, 0x006C, 0x032D}, + { 0x1E3B, 0x006C, 0x0331}, + { 0x1E3F, 0x006D, 0x0301}, + { 0x1E41, 0x006D, 0x0307}, + { 0x1E43, 0x006D, 0x0323}, + { 0x01F9, 0x006E, 0x0300}, + { 0x0144, 0x006E, 0x0301}, + { 0x00F1, 0x006E, 0x0303}, + { 0x1E45, 0x006E, 0x0307}, + { 0x0148, 0x006E, 0x030C}, + { 0x1E47, 0x006E, 0x0323}, + { 0x0146, 0x006E, 0x0327}, + { 0x1E4B, 0x006E, 0x032D}, + { 0x1E49, 0x006E, 0x0331}, + { 0x00F2, 0x006F, 0x0300}, + { 0x00F3, 0x006F, 0x0301}, + { 0x00F4, 0x006F, 0x0302}, + { 0x00F5, 0x006F, 0x0303}, + { 0x014D, 0x006F, 0x0304}, + { 0x014F, 0x006F, 0x0306}, + { 0x022F, 0x006F, 0x0307}, + { 0x00F6, 0x006F, 0x0308}, + { 0x1ECF, 0x006F, 0x0309}, + { 0x0151, 0x006F, 0x030B}, + { 0x01D2, 0x006F, 0x030C}, + { 0x020D, 0x006F, 0x030F}, + { 0x020F, 0x006F, 0x0311}, + { 0x01A1, 0x006F, 0x031B}, + { 0x1ECD, 0x006F, 0x0323}, + { 0x01EB, 0x006F, 0x0328}, + { 0x1E55, 0x0070, 0x0301}, + { 0x1E57, 0x0070, 0x0307}, + { 0x0155, 0x0072, 0x0301}, + { 0x1E59, 0x0072, 0x0307}, + { 0x0159, 0x0072, 0x030C}, + { 0x0211, 0x0072, 0x030F}, + { 0x0213, 0x0072, 0x0311}, + { 0x1E5B, 0x0072, 0x0323}, + { 0x0157, 0x0072, 0x0327}, + { 0x1E5F, 0x0072, 0x0331}, + { 0x015B, 0x0073, 0x0301}, + { 0x015D, 0x0073, 0x0302}, + { 0x1E61, 0x0073, 0x0307}, + { 0x0161, 0x0073, 0x030C}, + { 0x1E63, 0x0073, 0x0323}, + { 0x0219, 0x0073, 0x0326}, + { 0x015F, 0x0073, 0x0327}, + { 0x1E6B, 0x0074, 0x0307}, + { 0x1E97, 0x0074, 0x0308}, + { 0x0165, 0x0074, 0x030C}, + { 0x1E6D, 0x0074, 0x0323}, + { 0x021B, 0x0074, 0x0326}, + { 0x0163, 0x0074, 0x0327}, + { 0x1E71, 0x0074, 0x032D}, + { 0x1E6F, 0x0074, 0x0331}, + { 0x00F9, 0x0075, 0x0300}, + { 0x00FA, 0x0075, 0x0301}, + { 0x00FB, 0x0075, 0x0302}, + { 0x0169, 0x0075, 0x0303}, + { 0x016B, 0x0075, 0x0304}, + { 0x016D, 0x0075, 0x0306}, + { 0x00FC, 0x0075, 0x0308}, + { 0x1EE7, 0x0075, 0x0309}, + { 0x016F, 0x0075, 0x030A}, + { 0x0171, 0x0075, 0x030B}, + { 0x01D4, 0x0075, 0x030C}, + { 0x0215, 0x0075, 0x030F}, + { 0x0217, 0x0075, 0x0311}, + { 0x01B0, 0x0075, 0x031B}, + { 0x1EE5, 0x0075, 0x0323}, + { 0x1E73, 0x0075, 0x0324}, + { 0x0173, 0x0075, 0x0328}, + { 0x1E77, 0x0075, 0x032D}, + { 0x1E75, 0x0075, 0x0330}, + { 0x1E7D, 0x0076, 0x0303}, + { 0x1E7F, 0x0076, 0x0323}, + { 0x1E81, 0x0077, 0x0300}, + { 0x1E83, 0x0077, 0x0301}, + { 0x0175, 0x0077, 0x0302}, + { 0x1E87, 0x0077, 0x0307}, + { 0x1E85, 0x0077, 0x0308}, + { 0x1E98, 0x0077, 0x030A}, + { 0x1E89, 0x0077, 0x0323}, + { 0x1E8B, 0x0078, 0x0307}, + { 0x1E8D, 0x0078, 0x0308}, + { 0x1EF3, 0x0079, 0x0300}, + { 0x00FD, 0x0079, 0x0301}, + { 0x0177, 0x0079, 0x0302}, + { 0x1EF9, 0x0079, 0x0303}, + { 0x0233, 0x0079, 0x0304}, + { 0x1E8F, 0x0079, 0x0307}, + { 0x00FF, 0x0079, 0x0308}, + { 0x1EF7, 0x0079, 0x0309}, + { 0x1E99, 0x0079, 0x030A}, + { 0x1EF5, 0x0079, 0x0323}, + { 0x017A, 0x007A, 0x0301}, + { 0x1E91, 0x007A, 0x0302}, + { 0x017C, 0x007A, 0x0307}, + { 0x017E, 0x007A, 0x030C}, + { 0x1E93, 0x007A, 0x0323}, + { 0x1E95, 0x007A, 0x0331}, + { 0x1FED, 0x00A8, 0x0300}, + { 0x0385, 0x00A8, 0x0301}, + { 0x1FC1, 0x00A8, 0x0342}, + { 0x1EA6, 0x00C2, 0x0300}, + { 0x1EA4, 0x00C2, 0x0301}, + { 0x1EAA, 0x00C2, 0x0303}, + { 0x1EA8, 0x00C2, 0x0309}, + { 0x01DE, 0x00C4, 0x0304}, + { 0x01FA, 0x00C5, 0x0301}, + { 0x01FC, 0x00C6, 0x0301}, + { 0x01E2, 0x00C6, 0x0304}, + { 0x1E08, 0x00C7, 0x0301}, + { 0x1EC0, 0x00CA, 0x0300}, + { 0x1EBE, 0x00CA, 0x0301}, + { 0x1EC4, 0x00CA, 0x0303}, + { 0x1EC2, 0x00CA, 0x0309}, + { 0x1E2E, 0x00CF, 0x0301}, + { 0x1ED2, 0x00D4, 0x0300}, + { 0x1ED0, 0x00D4, 0x0301}, + { 0x1ED6, 0x00D4, 0x0303}, + { 0x1ED4, 0x00D4, 0x0309}, + { 0x1E4C, 0x00D5, 0x0301}, + { 0x022C, 0x00D5, 0x0304}, + { 0x1E4E, 0x00D5, 0x0308}, + { 0x022A, 0x00D6, 0x0304}, + { 0x01FE, 0x00D8, 0x0301}, + { 0x01DB, 0x00DC, 0x0300}, + { 0x01D7, 0x00DC, 0x0301}, + { 0x01D5, 0x00DC, 0x0304}, + { 0x01D9, 0x00DC, 0x030C}, + { 0x1EA7, 0x00E2, 0x0300}, + { 0x1EA5, 0x00E2, 0x0301}, + { 0x1EAB, 0x00E2, 0x0303}, + { 0x1EA9, 0x00E2, 0x0309}, + { 0x01DF, 0x00E4, 0x0304}, + { 0x01FB, 0x00E5, 0x0301}, + { 0x01FD, 0x00E6, 0x0301}, + { 0x01E3, 0x00E6, 0x0304}, + { 0x1E09, 0x00E7, 0x0301}, + { 0x1EC1, 0x00EA, 0x0300}, + { 0x1EBF, 0x00EA, 0x0301}, + { 0x1EC5, 0x00EA, 0x0303}, + { 0x1EC3, 0x00EA, 0x0309}, + { 0x1E2F, 0x00EF, 0x0301}, + { 0x1ED3, 0x00F4, 0x0300}, + { 0x1ED1, 0x00F4, 0x0301}, + { 0x1ED7, 0x00F4, 0x0303}, + { 0x1ED5, 0x00F4, 0x0309}, + { 0x1E4D, 0x00F5, 0x0301}, + { 0x022D, 0x00F5, 0x0304}, + { 0x1E4F, 0x00F5, 0x0308}, + { 0x022B, 0x00F6, 0x0304}, + { 0x01FF, 0x00F8, 0x0301}, + { 0x01DC, 0x00FC, 0x0300}, + { 0x01D8, 0x00FC, 0x0301}, + { 0x01D6, 0x00FC, 0x0304}, + { 0x01DA, 0x00FC, 0x030C}, + { 0x1EB0, 0x0102, 0x0300}, + { 0x1EAE, 0x0102, 0x0301}, + { 0x1EB4, 0x0102, 0x0303}, + { 0x1EB2, 0x0102, 0x0309}, + { 0x1EB1, 0x0103, 0x0300}, + { 0x1EAF, 0x0103, 0x0301}, + { 0x1EB5, 0x0103, 0x0303}, + { 0x1EB3, 0x0103, 0x0309}, + { 0x1E14, 0x0112, 0x0300}, + { 0x1E16, 0x0112, 0x0301}, + { 0x1E15, 0x0113, 0x0300}, + { 0x1E17, 0x0113, 0x0301}, + { 0x1E50, 0x014C, 0x0300}, + { 0x1E52, 0x014C, 0x0301}, + { 0x1E51, 0x014D, 0x0300}, + { 0x1E53, 0x014D, 0x0301}, + { 0x1E64, 0x015A, 0x0307}, + { 0x1E65, 0x015B, 0x0307}, + { 0x1E66, 0x0160, 0x0307}, + { 0x1E67, 0x0161, 0x0307}, + { 0x1E78, 0x0168, 0x0301}, + { 0x1E79, 0x0169, 0x0301}, + { 0x1E7A, 0x016A, 0x0308}, + { 0x1E7B, 0x016B, 0x0308}, + { 0x1E9B, 0x017F, 0x0307}, + { 0x1EDC, 0x01A0, 0x0300}, + { 0x1EDA, 0x01A0, 0x0301}, + { 0x1EE0, 0x01A0, 0x0303}, + { 0x1EDE, 0x01A0, 0x0309}, + { 0x1EE2, 0x01A0, 0x0323}, + { 0x1EDD, 0x01A1, 0x0300}, + { 0x1EDB, 0x01A1, 0x0301}, + { 0x1EE1, 0x01A1, 0x0303}, + { 0x1EDF, 0x01A1, 0x0309}, + { 0x1EE3, 0x01A1, 0x0323}, + { 0x1EEA, 0x01AF, 0x0300}, + { 0x1EE8, 0x01AF, 0x0301}, + { 0x1EEE, 0x01AF, 0x0303}, + { 0x1EEC, 0x01AF, 0x0309}, + { 0x1EF0, 0x01AF, 0x0323}, + { 0x1EEB, 0x01B0, 0x0300}, + { 0x1EE9, 0x01B0, 0x0301}, + { 0x1EEF, 0x01B0, 0x0303}, + { 0x1EED, 0x01B0, 0x0309}, + { 0x1EF1, 0x01B0, 0x0323}, + { 0x01EE, 0x01B7, 0x030C}, + { 0x01EC, 0x01EA, 0x0304}, + { 0x01ED, 0x01EB, 0x0304}, + { 0x01E0, 0x0226, 0x0304}, + { 0x01E1, 0x0227, 0x0304}, + { 0x1E1C, 0x0228, 0x0306}, + { 0x1E1D, 0x0229, 0x0306}, + { 0x0230, 0x022E, 0x0304}, + { 0x0231, 0x022F, 0x0304}, + { 0x01EF, 0x0292, 0x030C}, + { 0x0344, 0x0308, 0x0301}, + { 0x1FBA, 0x0391, 0x0300}, + { 0x0386, 0x0391, 0x0301}, + { 0x1FB9, 0x0391, 0x0304}, + { 0x1FB8, 0x0391, 0x0306}, + { 0x1F08, 0x0391, 0x0313}, + { 0x1F09, 0x0391, 0x0314}, + { 0x1FBC, 0x0391, 0x0345}, + { 0x1FC8, 0x0395, 0x0300}, + { 0x0388, 0x0395, 0x0301}, + { 0x1F18, 0x0395, 0x0313}, + { 0x1F19, 0x0395, 0x0314}, + { 0x1FCA, 0x0397, 0x0300}, + { 0x0389, 0x0397, 0x0301}, + { 0x1F28, 0x0397, 0x0313}, + { 0x1F29, 0x0397, 0x0314}, + { 0x1FCC, 0x0397, 0x0345}, + { 0x1FDA, 0x0399, 0x0300}, + { 0x038A, 0x0399, 0x0301}, + { 0x1FD9, 0x0399, 0x0304}, + { 0x1FD8, 0x0399, 0x0306}, + { 0x03AA, 0x0399, 0x0308}, + { 0x1F38, 0x0399, 0x0313}, + { 0x1F39, 0x0399, 0x0314}, + { 0x1FF8, 0x039F, 0x0300}, + { 0x038C, 0x039F, 0x0301}, + { 0x1F48, 0x039F, 0x0313}, + { 0x1F49, 0x039F, 0x0314}, + { 0x1FEC, 0x03A1, 0x0314}, + { 0x1FEA, 0x03A5, 0x0300}, + { 0x038E, 0x03A5, 0x0301}, + { 0x1FE9, 0x03A5, 0x0304}, + { 0x1FE8, 0x03A5, 0x0306}, + { 0x03AB, 0x03A5, 0x0308}, + { 0x1F59, 0x03A5, 0x0314}, + { 0x1FFA, 0x03A9, 0x0300}, + { 0x038F, 0x03A9, 0x0301}, + { 0x1F68, 0x03A9, 0x0313}, + { 0x1F69, 0x03A9, 0x0314}, + { 0x1FFC, 0x03A9, 0x0345}, + { 0x1FB4, 0x03AC, 0x0345}, + { 0x1FC4, 0x03AE, 0x0345}, + { 0x1F70, 0x03B1, 0x0300}, + { 0x03AC, 0x03B1, 0x0301}, + { 0x1FB1, 0x03B1, 0x0304}, + { 0x1FB0, 0x03B1, 0x0306}, + { 0x1F00, 0x03B1, 0x0313}, + { 0x1F01, 0x03B1, 0x0314}, + { 0x1FB6, 0x03B1, 0x0342}, + { 0x1FB3, 0x03B1, 0x0345}, + { 0x1F72, 0x03B5, 0x0300}, + { 0x03AD, 0x03B5, 0x0301}, + { 0x1F10, 0x03B5, 0x0313}, + { 0x1F11, 0x03B5, 0x0314}, + { 0x1F74, 0x03B7, 0x0300}, + { 0x03AE, 0x03B7, 0x0301}, + { 0x1F20, 0x03B7, 0x0313}, + { 0x1F21, 0x03B7, 0x0314}, + { 0x1FC6, 0x03B7, 0x0342}, + { 0x1FC3, 0x03B7, 0x0345}, + { 0x1F76, 0x03B9, 0x0300}, + { 0x03AF, 0x03B9, 0x0301}, + { 0x1FD1, 0x03B9, 0x0304}, + { 0x1FD0, 0x03B9, 0x0306}, + { 0x03CA, 0x03B9, 0x0308}, + { 0x1F30, 0x03B9, 0x0313}, + { 0x1F31, 0x03B9, 0x0314}, + { 0x1FD6, 0x03B9, 0x0342}, + { 0x1F78, 0x03BF, 0x0300}, + { 0x03CC, 0x03BF, 0x0301}, + { 0x1F40, 0x03BF, 0x0313}, + { 0x1F41, 0x03BF, 0x0314}, + { 0x1FE4, 0x03C1, 0x0313}, + { 0x1FE5, 0x03C1, 0x0314}, + { 0x1F7A, 0x03C5, 0x0300}, + { 0x03CD, 0x03C5, 0x0301}, + { 0x1FE1, 0x03C5, 0x0304}, + { 0x1FE0, 0x03C5, 0x0306}, + { 0x03CB, 0x03C5, 0x0308}, + { 0x1F50, 0x03C5, 0x0313}, + { 0x1F51, 0x03C5, 0x0314}, + { 0x1FE6, 0x03C5, 0x0342}, + { 0x1F7C, 0x03C9, 0x0300}, + { 0x03CE, 0x03C9, 0x0301}, + { 0x1F60, 0x03C9, 0x0313}, + { 0x1F61, 0x03C9, 0x0314}, + { 0x1FF6, 0x03C9, 0x0342}, + { 0x1FF3, 0x03C9, 0x0345}, + { 0x1FD2, 0x03CA, 0x0300}, + { 0x0390, 0x03CA, 0x0301}, + { 0x1FD7, 0x03CA, 0x0342}, + { 0x1FE2, 0x03CB, 0x0300}, + { 0x03B0, 0x03CB, 0x0301}, + { 0x1FE7, 0x03CB, 0x0342}, + { 0x1FF4, 0x03CE, 0x0345}, + { 0x03D3, 0x03D2, 0x0301}, + { 0x03D4, 0x03D2, 0x0308}, + { 0x0407, 0x0406, 0x0308}, + { 0x04D0, 0x0410, 0x0306}, + { 0x04D2, 0x0410, 0x0308}, + { 0x0403, 0x0413, 0x0301}, + { 0x0400, 0x0415, 0x0300}, + { 0x04D6, 0x0415, 0x0306}, + { 0x0401, 0x0415, 0x0308}, + { 0x04C1, 0x0416, 0x0306}, + { 0x04DC, 0x0416, 0x0308}, + { 0x04DE, 0x0417, 0x0308}, + { 0x040D, 0x0418, 0x0300}, + { 0x04E2, 0x0418, 0x0304}, + { 0x0419, 0x0418, 0x0306}, + { 0x04E4, 0x0418, 0x0308}, + { 0x040C, 0x041A, 0x0301}, + { 0x04E6, 0x041E, 0x0308}, + { 0x04EE, 0x0423, 0x0304}, + { 0x040E, 0x0423, 0x0306}, + { 0x04F0, 0x0423, 0x0308}, + { 0x04F2, 0x0423, 0x030B}, + { 0x04F4, 0x0427, 0x0308}, + { 0x04F8, 0x042B, 0x0308}, + { 0x04EC, 0x042D, 0x0308}, + { 0x04D1, 0x0430, 0x0306}, + { 0x04D3, 0x0430, 0x0308}, + { 0x0453, 0x0433, 0x0301}, + { 0x0450, 0x0435, 0x0300}, + { 0x04D7, 0x0435, 0x0306}, + { 0x0451, 0x0435, 0x0308}, + { 0x04C2, 0x0436, 0x0306}, + { 0x04DD, 0x0436, 0x0308}, + { 0x04DF, 0x0437, 0x0308}, + { 0x045D, 0x0438, 0x0300}, + { 0x04E3, 0x0438, 0x0304}, + { 0x0439, 0x0438, 0x0306}, + { 0x04E5, 0x0438, 0x0308}, + { 0x045C, 0x043A, 0x0301}, + { 0x04E7, 0x043E, 0x0308}, + { 0x04EF, 0x0443, 0x0304}, + { 0x045E, 0x0443, 0x0306}, + { 0x04F1, 0x0443, 0x0308}, + { 0x04F3, 0x0443, 0x030B}, + { 0x04F5, 0x0447, 0x0308}, + { 0x04F9, 0x044B, 0x0308}, + { 0x04ED, 0x044D, 0x0308}, + { 0x0457, 0x0456, 0x0308}, + { 0x0476, 0x0474, 0x030F}, + { 0x0477, 0x0475, 0x030F}, + { 0x04DA, 0x04D8, 0x0308}, + { 0x04DB, 0x04D9, 0x0308}, + { 0x04EA, 0x04E8, 0x0308}, + { 0x04EB, 0x04E9, 0x0308}, + { 0xFB2E, 0x05D0, 0x05B7}, + { 0xFB2F, 0x05D0, 0x05B8}, + { 0xFB30, 0x05D0, 0x05BC}, + { 0xFB31, 0x05D1, 0x05BC}, + { 0xFB4C, 0x05D1, 0x05BF}, + { 0xFB32, 0x05D2, 0x05BC}, + { 0xFB33, 0x05D3, 0x05BC}, + { 0xFB34, 0x05D4, 0x05BC}, + { 0xFB4B, 0x05D5, 0x05B9}, + { 0xFB35, 0x05D5, 0x05BC}, + { 0xFB36, 0x05D6, 0x05BC}, + { 0xFB38, 0x05D8, 0x05BC}, + { 0xFB1D, 0x05D9, 0x05B4}, + { 0xFB39, 0x05D9, 0x05BC}, + { 0xFB3A, 0x05DA, 0x05BC}, + { 0xFB3B, 0x05DB, 0x05BC}, + { 0xFB4D, 0x05DB, 0x05BF}, + { 0xFB3C, 0x05DC, 0x05BC}, + { 0xFB3E, 0x05DE, 0x05BC}, + { 0xFB40, 0x05E0, 0x05BC}, + { 0xFB41, 0x05E1, 0x05BC}, + { 0xFB43, 0x05E3, 0x05BC}, + { 0xFB44, 0x05E4, 0x05BC}, + { 0xFB4E, 0x05E4, 0x05BF}, + { 0xFB46, 0x05E6, 0x05BC}, + { 0xFB47, 0x05E7, 0x05BC}, + { 0xFB48, 0x05E8, 0x05BC}, + { 0xFB49, 0x05E9, 0x05BC}, + { 0xFB2A, 0x05E9, 0x05C1}, + { 0xFB2B, 0x05E9, 0x05C2}, + { 0xFB4A, 0x05EA, 0x05BC}, + { 0xFB1F, 0x05F2, 0x05B7}, + { 0x0622, 0x0627, 0x0653}, + { 0x0623, 0x0627, 0x0654}, + { 0x0625, 0x0627, 0x0655}, + { 0x0624, 0x0648, 0x0654}, + { 0x0626, 0x064A, 0x0654}, + { 0x06C2, 0x06C1, 0x0654}, + { 0x06D3, 0x06D2, 0x0654}, + { 0x06C0, 0x06D5, 0x0654}, + { 0x0958, 0x0915, 0x093C}, + { 0x0959, 0x0916, 0x093C}, + { 0x095A, 0x0917, 0x093C}, + { 0x095B, 0x091C, 0x093C}, + { 0x095C, 0x0921, 0x093C}, + { 0x095D, 0x0922, 0x093C}, + { 0x0929, 0x0928, 0x093C}, + { 0x095E, 0x092B, 0x093C}, + { 0x095F, 0x092F, 0x093C}, + { 0x0931, 0x0930, 0x093C}, + { 0x0934, 0x0933, 0x093C}, + { 0x09DC, 0x09A1, 0x09BC}, + { 0x09DD, 0x09A2, 0x09BC}, + { 0x09DF, 0x09AF, 0x09BC}, + { 0x09CB, 0x09C7, 0x09BE}, + { 0x09CC, 0x09C7, 0x09D7}, + { 0x0A59, 0x0A16, 0x0A3C}, + { 0x0A5A, 0x0A17, 0x0A3C}, + { 0x0A5B, 0x0A1C, 0x0A3C}, + { 0x0A5E, 0x0A2B, 0x0A3C}, + { 0x0A33, 0x0A32, 0x0A3C}, + { 0x0A36, 0x0A38, 0x0A3C}, + { 0x0B5C, 0x0B21, 0x0B3C}, + { 0x0B5D, 0x0B22, 0x0B3C}, + { 0x0B4B, 0x0B47, 0x0B3E}, + { 0x0B48, 0x0B47, 0x0B56}, + { 0x0B4C, 0x0B47, 0x0B57}, + { 0x0B94, 0x0B92, 0x0BD7}, + { 0x0BCA, 0x0BC6, 0x0BBE}, + { 0x0BCC, 0x0BC6, 0x0BD7}, + { 0x0BCB, 0x0BC7, 0x0BBE}, + { 0x0C48, 0x0C46, 0x0C56}, + { 0x0CC0, 0x0CBF, 0x0CD5}, + { 0x0CCA, 0x0CC6, 0x0CC2}, + { 0x0CC7, 0x0CC6, 0x0CD5}, + { 0x0CC8, 0x0CC6, 0x0CD6}, + { 0x0CCB, 0x0CCA, 0x0CD5}, + { 0x0D4A, 0x0D46, 0x0D3E}, + { 0x0D4C, 0x0D46, 0x0D57}, + { 0x0D4B, 0x0D47, 0x0D3E}, + { 0x0DDA, 0x0DD9, 0x0DCA}, + { 0x0DDC, 0x0DD9, 0x0DCF}, + { 0x0DDE, 0x0DD9, 0x0DDF}, + { 0x0DDD, 0x0DDC, 0x0DCA}, + { 0x0F69, 0x0F40, 0x0FB5}, + { 0x0F43, 0x0F42, 0x0FB7}, + { 0x0F4D, 0x0F4C, 0x0FB7}, + { 0x0F52, 0x0F51, 0x0FB7}, + { 0x0F57, 0x0F56, 0x0FB7}, + { 0x0F5C, 0x0F5B, 0x0FB7}, + { 0x0F73, 0x0F71, 0x0F72}, + { 0x0F75, 0x0F71, 0x0F74}, + { 0x0F81, 0x0F71, 0x0F80}, + { 0x0FB9, 0x0F90, 0x0FB5}, + { 0x0F93, 0x0F92, 0x0FB7}, + { 0x0F9D, 0x0F9C, 0x0FB7}, + { 0x0FA2, 0x0FA1, 0x0FB7}, + { 0x0FA7, 0x0FA6, 0x0FB7}, + { 0x0FAC, 0x0FAB, 0x0FB7}, + { 0x0F76, 0x0FB2, 0x0F80}, + { 0x0F78, 0x0FB3, 0x0F80}, + { 0x1026, 0x1025, 0x102E}, + { 0x1B06, 0x1B05, 0x1B35}, + { 0x1B08, 0x1B07, 0x1B35}, + { 0x1B0A, 0x1B09, 0x1B35}, + { 0x1B0C, 0x1B0B, 0x1B35}, + { 0x1B0E, 0x1B0D, 0x1B35}, + { 0x1B12, 0x1B11, 0x1B35}, + { 0x1B3B, 0x1B3A, 0x1B35}, + { 0x1B3D, 0x1B3C, 0x1B35}, + { 0x1B40, 0x1B3E, 0x1B35}, + { 0x1B41, 0x1B3F, 0x1B35}, + { 0x1B43, 0x1B42, 0x1B35}, + { 0x1E38, 0x1E36, 0x0304}, + { 0x1E39, 0x1E37, 0x0304}, + { 0x1E5C, 0x1E5A, 0x0304}, + { 0x1E5D, 0x1E5B, 0x0304}, + { 0x1E68, 0x1E62, 0x0307}, + { 0x1E69, 0x1E63, 0x0307}, + { 0x1EAC, 0x1EA0, 0x0302}, + { 0x1EB6, 0x1EA0, 0x0306}, + { 0x1EAD, 0x1EA1, 0x0302}, + { 0x1EB7, 0x1EA1, 0x0306}, + { 0x1EC6, 0x1EB8, 0x0302}, + { 0x1EC7, 0x1EB9, 0x0302}, + { 0x1ED8, 0x1ECC, 0x0302}, + { 0x1ED9, 0x1ECD, 0x0302}, + { 0x1F02, 0x1F00, 0x0300}, + { 0x1F04, 0x1F00, 0x0301}, + { 0x1F06, 0x1F00, 0x0342}, + { 0x1F80, 0x1F00, 0x0345}, + { 0x1F03, 0x1F01, 0x0300}, + { 0x1F05, 0x1F01, 0x0301}, + { 0x1F07, 0x1F01, 0x0342}, + { 0x1F81, 0x1F01, 0x0345}, + { 0x1F82, 0x1F02, 0x0345}, + { 0x1F83, 0x1F03, 0x0345}, + { 0x1F84, 0x1F04, 0x0345}, + { 0x1F85, 0x1F05, 0x0345}, + { 0x1F86, 0x1F06, 0x0345}, + { 0x1F87, 0x1F07, 0x0345}, + { 0x1F0A, 0x1F08, 0x0300}, + { 0x1F0C, 0x1F08, 0x0301}, + { 0x1F0E, 0x1F08, 0x0342}, + { 0x1F88, 0x1F08, 0x0345}, + { 0x1F0B, 0x1F09, 0x0300}, + { 0x1F0D, 0x1F09, 0x0301}, + { 0x1F0F, 0x1F09, 0x0342}, + { 0x1F89, 0x1F09, 0x0345}, + { 0x1F8A, 0x1F0A, 0x0345}, + { 0x1F8B, 0x1F0B, 0x0345}, + { 0x1F8C, 0x1F0C, 0x0345}, + { 0x1F8D, 0x1F0D, 0x0345}, + { 0x1F8E, 0x1F0E, 0x0345}, + { 0x1F8F, 0x1F0F, 0x0345}, + { 0x1F12, 0x1F10, 0x0300}, + { 0x1F14, 0x1F10, 0x0301}, + { 0x1F13, 0x1F11, 0x0300}, + { 0x1F15, 0x1F11, 0x0301}, + { 0x1F1A, 0x1F18, 0x0300}, + { 0x1F1C, 0x1F18, 0x0301}, + { 0x1F1B, 0x1F19, 0x0300}, + { 0x1F1D, 0x1F19, 0x0301}, + { 0x1F22, 0x1F20, 0x0300}, + { 0x1F24, 0x1F20, 0x0301}, + { 0x1F26, 0x1F20, 0x0342}, + { 0x1F90, 0x1F20, 0x0345}, + { 0x1F23, 0x1F21, 0x0300}, + { 0x1F25, 0x1F21, 0x0301}, + { 0x1F27, 0x1F21, 0x0342}, + { 0x1F91, 0x1F21, 0x0345}, + { 0x1F92, 0x1F22, 0x0345}, + { 0x1F93, 0x1F23, 0x0345}, + { 0x1F94, 0x1F24, 0x0345}, + { 0x1F95, 0x1F25, 0x0345}, + { 0x1F96, 0x1F26, 0x0345}, + { 0x1F97, 0x1F27, 0x0345}, + { 0x1F2A, 0x1F28, 0x0300}, + { 0x1F2C, 0x1F28, 0x0301}, + { 0x1F2E, 0x1F28, 0x0342}, + { 0x1F98, 0x1F28, 0x0345}, + { 0x1F2B, 0x1F29, 0x0300}, + { 0x1F2D, 0x1F29, 0x0301}, + { 0x1F2F, 0x1F29, 0x0342}, + { 0x1F99, 0x1F29, 0x0345}, + { 0x1F9A, 0x1F2A, 0x0345}, + { 0x1F9B, 0x1F2B, 0x0345}, + { 0x1F9C, 0x1F2C, 0x0345}, + { 0x1F9D, 0x1F2D, 0x0345}, + { 0x1F9E, 0x1F2E, 0x0345}, + { 0x1F9F, 0x1F2F, 0x0345}, + { 0x1F32, 0x1F30, 0x0300}, + { 0x1F34, 0x1F30, 0x0301}, + { 0x1F36, 0x1F30, 0x0342}, + { 0x1F33, 0x1F31, 0x0300}, + { 0x1F35, 0x1F31, 0x0301}, + { 0x1F37, 0x1F31, 0x0342}, + { 0x1F3A, 0x1F38, 0x0300}, + { 0x1F3C, 0x1F38, 0x0301}, + { 0x1F3E, 0x1F38, 0x0342}, + { 0x1F3B, 0x1F39, 0x0300}, + { 0x1F3D, 0x1F39, 0x0301}, + { 0x1F3F, 0x1F39, 0x0342}, + { 0x1F42, 0x1F40, 0x0300}, + { 0x1F44, 0x1F40, 0x0301}, + { 0x1F43, 0x1F41, 0x0300}, + { 0x1F45, 0x1F41, 0x0301}, + { 0x1F4A, 0x1F48, 0x0300}, + { 0x1F4C, 0x1F48, 0x0301}, + { 0x1F4B, 0x1F49, 0x0300}, + { 0x1F4D, 0x1F49, 0x0301}, + { 0x1F52, 0x1F50, 0x0300}, + { 0x1F54, 0x1F50, 0x0301}, + { 0x1F56, 0x1F50, 0x0342}, + { 0x1F53, 0x1F51, 0x0300}, + { 0x1F55, 0x1F51, 0x0301}, + { 0x1F57, 0x1F51, 0x0342}, + { 0x1F5B, 0x1F59, 0x0300}, + { 0x1F5D, 0x1F59, 0x0301}, + { 0x1F5F, 0x1F59, 0x0342}, + { 0x1F62, 0x1F60, 0x0300}, + { 0x1F64, 0x1F60, 0x0301}, + { 0x1F66, 0x1F60, 0x0342}, + { 0x1FA0, 0x1F60, 0x0345}, + { 0x1F63, 0x1F61, 0x0300}, + { 0x1F65, 0x1F61, 0x0301}, + { 0x1F67, 0x1F61, 0x0342}, + { 0x1FA1, 0x1F61, 0x0345}, + { 0x1FA2, 0x1F62, 0x0345}, + { 0x1FA3, 0x1F63, 0x0345}, + { 0x1FA4, 0x1F64, 0x0345}, + { 0x1FA5, 0x1F65, 0x0345}, + { 0x1FA6, 0x1F66, 0x0345}, + { 0x1FA7, 0x1F67, 0x0345}, + { 0x1F6A, 0x1F68, 0x0300}, + { 0x1F6C, 0x1F68, 0x0301}, + { 0x1F6E, 0x1F68, 0x0342}, + { 0x1FA8, 0x1F68, 0x0345}, + { 0x1F6B, 0x1F69, 0x0300}, + { 0x1F6D, 0x1F69, 0x0301}, + { 0x1F6F, 0x1F69, 0x0342}, + { 0x1FA9, 0x1F69, 0x0345}, + { 0x1FAA, 0x1F6A, 0x0345}, + { 0x1FAB, 0x1F6B, 0x0345}, + { 0x1FAC, 0x1F6C, 0x0345}, + { 0x1FAD, 0x1F6D, 0x0345}, + { 0x1FAE, 0x1F6E, 0x0345}, + { 0x1FAF, 0x1F6F, 0x0345}, + { 0x1FB2, 0x1F70, 0x0345}, + { 0x1FC2, 0x1F74, 0x0345}, + { 0x1FF2, 0x1F7C, 0x0345}, + { 0x1FB7, 0x1FB6, 0x0345}, + { 0x1FCD, 0x1FBF, 0x0300}, + { 0x1FCE, 0x1FBF, 0x0301}, + { 0x1FCF, 0x1FBF, 0x0342}, + { 0x1FC7, 0x1FC6, 0x0345}, + { 0x1FF7, 0x1FF6, 0x0345}, + { 0x1FDD, 0x1FFE, 0x0300}, + { 0x1FDE, 0x1FFE, 0x0301}, + { 0x1FDF, 0x1FFE, 0x0342}, + { 0x219A, 0x2190, 0x0338}, + { 0x219B, 0x2192, 0x0338}, + { 0x21AE, 0x2194, 0x0338}, + { 0x21CD, 0x21D0, 0x0338}, + { 0x21CF, 0x21D2, 0x0338}, + { 0x21CE, 0x21D4, 0x0338}, + { 0x2204, 0x2203, 0x0338}, + { 0x2209, 0x2208, 0x0338}, + { 0x220C, 0x220B, 0x0338}, + { 0x2224, 0x2223, 0x0338}, + { 0x2226, 0x2225, 0x0338}, + { 0x2241, 0x223C, 0x0338}, + { 0x2244, 0x2243, 0x0338}, + { 0x2247, 0x2245, 0x0338}, + { 0x2249, 0x2248, 0x0338}, + { 0x226D, 0x224D, 0x0338}, + { 0x2262, 0x2261, 0x0338}, + { 0x2270, 0x2264, 0x0338}, + { 0x2271, 0x2265, 0x0338}, + { 0x2274, 0x2272, 0x0338}, + { 0x2275, 0x2273, 0x0338}, + { 0x2278, 0x2276, 0x0338}, + { 0x2279, 0x2277, 0x0338}, + { 0x2280, 0x227A, 0x0338}, + { 0x2281, 0x227B, 0x0338}, + { 0x22E0, 0x227C, 0x0338}, + { 0x22E1, 0x227D, 0x0338}, + { 0x2284, 0x2282, 0x0338}, + { 0x2285, 0x2283, 0x0338}, + { 0x2288, 0x2286, 0x0338}, + { 0x2289, 0x2287, 0x0338}, + { 0x22E2, 0x2291, 0x0338}, + { 0x22E3, 0x2292, 0x0338}, + { 0x22AC, 0x22A2, 0x0338}, + { 0x22AD, 0x22A8, 0x0338}, + { 0x22AE, 0x22A9, 0x0338}, + { 0x22AF, 0x22AB, 0x0338}, + { 0x22EA, 0x22B2, 0x0338}, + { 0x22EB, 0x22B3, 0x0338}, + { 0x22EC, 0x22B4, 0x0338}, + { 0x22ED, 0x22B5, 0x0338}, + { 0x2ADC, 0x2ADD, 0x0338}, + { 0x3094, 0x3046, 0x3099}, + { 0x304C, 0x304B, 0x3099}, + { 0x304E, 0x304D, 0x3099}, + { 0x3050, 0x304F, 0x3099}, + { 0x3052, 0x3051, 0x3099}, + { 0x3054, 0x3053, 0x3099}, + { 0x3056, 0x3055, 0x3099}, + { 0x3058, 0x3057, 0x3099}, + { 0x305A, 0x3059, 0x3099}, + { 0x305C, 0x305B, 0x3099}, + { 0x305E, 0x305D, 0x3099}, + { 0x3060, 0x305F, 0x3099}, + { 0x3062, 0x3061, 0x3099}, + { 0x3065, 0x3064, 0x3099}, + { 0x3067, 0x3066, 0x3099}, + { 0x3069, 0x3068, 0x3099}, + { 0x3070, 0x306F, 0x3099}, + { 0x3071, 0x306F, 0x309A}, + { 0x3073, 0x3072, 0x3099}, + { 0x3074, 0x3072, 0x309A}, + { 0x3076, 0x3075, 0x3099}, + { 0x3077, 0x3075, 0x309A}, + { 0x3079, 0x3078, 0x3099}, + { 0x307A, 0x3078, 0x309A}, + { 0x307C, 0x307B, 0x3099}, + { 0x307D, 0x307B, 0x309A}, + { 0x309E, 0x309D, 0x3099}, + { 0x30F4, 0x30A6, 0x3099}, + { 0x30AC, 0x30AB, 0x3099}, + { 0x30AE, 0x30AD, 0x3099}, + { 0x30B0, 0x30AF, 0x3099}, + { 0x30B2, 0x30B1, 0x3099}, + { 0x30B4, 0x30B3, 0x3099}, + { 0x30B6, 0x30B5, 0x3099}, + { 0x30B8, 0x30B7, 0x3099}, + { 0x30BA, 0x30B9, 0x3099}, + { 0x30BC, 0x30BB, 0x3099}, + { 0x30BE, 0x30BD, 0x3099}, + { 0x30C0, 0x30BF, 0x3099}, + { 0x30C2, 0x30C1, 0x3099}, + { 0x30C5, 0x30C4, 0x3099}, + { 0x30C7, 0x30C6, 0x3099}, + { 0x30C9, 0x30C8, 0x3099}, + { 0x30D0, 0x30CF, 0x3099}, + { 0x30D1, 0x30CF, 0x309A}, + { 0x30D3, 0x30D2, 0x3099}, + { 0x30D4, 0x30D2, 0x309A}, + { 0x30D6, 0x30D5, 0x3099}, + { 0x30D7, 0x30D5, 0x309A}, + { 0x30D9, 0x30D8, 0x3099}, + { 0x30DA, 0x30D8, 0x309A}, + { 0x30DC, 0x30DB, 0x3099}, + { 0x30DD, 0x30DB, 0x309A}, + { 0x30F7, 0x30EF, 0x3099}, + { 0x30F8, 0x30F0, 0x3099}, + { 0x30F9, 0x30F1, 0x3099}, + { 0x30FA, 0x30F2, 0x3099}, + { 0x30FE, 0x30FD, 0x3099}, + { 0xFB2C, 0xFB49, 0x05C1}, + { 0xFB2D, 0xFB49, 0x05C2}, + }; + + private static final int UNICODE_SHIFT = 21; + + public static char precompose(char base, char comb) { + int min = 0; + int max = precompositions.length - 1; + int mid; + + long sought = base << UNICODE_SHIFT | comb; + long that; + + while (max >= min) { + mid = (min + max) / 2; + that = precompositions[mid][1] << UNICODE_SHIFT | precompositions[mid][2]; + if (that < sought) + min = mid + 1; + else if (that > sought) + max = mid - 1; + else + return precompositions[mid][0]; + } + + // No match; return character without combiner + return base; + } +} diff --git a/app/src/main/java/de/mud/terminal/VDUBuffer.java b/app/src/main/java/de/mud/terminal/VDUBuffer.java new file mode 100644 index 0000000..93e3ccf --- /dev/null +++ b/app/src/main/java/de/mud/terminal/VDUBuffer.java @@ -0,0 +1,854 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Mei�ner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +import java.util.Arrays; + +/** + * Implementation of a Video Display Unit (VDU) buffer. This class contains + * all methods to manipulate the buffer that stores characters and their + * attributes as well as the regions displayed. + * + * @author Matthias L. Jugel, Marcus Meißner + * @version $Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $ + */ +public class VDUBuffer { + + /** The current version id tag */ + public final static String ID = "$Id: VDUBuffer.java 503 2005-10-24 07:34:13Z marcus $"; + + /** Enable debug messages. */ + public final static int debug = 0; + + public int height, width; /* rows and columns */ + public boolean[] update; /* contains the lines that need update */ + public char[][] charArray; /* contains the characters */ + public int[][] charAttributes; /* contains character attrs */ + public int bufSize; + public int maxBufSize; /* buffer sizes */ + public int screenBase; /* the actual screen start */ + public int windowBase; /* where the start displaying */ + public int scrollMarker; /* marks the last line inserted */ + + private int topMargin; /* top scroll margin */ + private int bottomMargin; /* bottom scroll margin */ + + // cursor variables + protected boolean showcursor = true; + protected int cursorX, cursorY; + + /** Scroll up when inserting a line. */ + public final static boolean SCROLL_UP = false; + /** Scroll down when inserting a line. */ + public final static boolean SCROLL_DOWN = true; + + /* Attributes bit-field usage: + * + * 8421 8421 8421 8421 8421 8421 8421 8421 + * |||| |||| |||| |||| |||| |||| |||| |||`- Bold + * |||| |||| |||| |||| |||| |||| |||| ||`-- Underline + * |||| |||| |||| |||| |||| |||| |||| |`--- Invert + * |||| |||| |||| |||| |||| |||| |||| `---- Low + * |||| |||| |||| |||| |||| |||| |||`------ Invisible + * |||| |||| |||| |||| ||`+-++++-+++------- Foreground Color + * |||| |||| |`++-++++-++------------------ Background Color + * |||| |||| `----------------------------- Fullwidth character + * `+++-++++------------------------------- Reserved for future use + */ + + /** Make character normal. */ + public final static int NORMAL = 0x00; + /** Make character bold. */ + public final static int BOLD = 0x01; + /** Underline character. */ + public final static int UNDERLINE = 0x02; + /** Invert character. */ + public final static int INVERT = 0x04; + /** Lower intensity character. */ + public final static int LOW = 0x08; + /** Invisible character. */ + public final static int INVISIBLE = 0x10; + /** Unicode full-width character (CJK, et al.) */ + public final static int FULLWIDTH = 0x8000000; + + /** how much to left shift the foreground color */ + public final static int COLOR_FG_SHIFT = 5; + /** how much to left shift the background color */ + public final static int COLOR_BG_SHIFT = 14; + /** color mask */ + public final static int COLOR = 0x7fffe0; /* 0000 0000 0111 1111 1111 1111 1110 0000 */ + /** foreground color mask */ + public final static int COLOR_FG = 0x3fe0; /* 0000 0000 0000 0000 0011 1111 1110 0000 */ + /** background color mask */ + public final static int COLOR_BG = 0x7fc000; /* 0000 0000 0111 1111 1100 0000 0000 0000 */ + + /** + * Create a new video display buffer with the passed width and height in + * characters. + * @param width the length of the character lines + * @param height the amount of lines on the screen + */ + public VDUBuffer(int width, int height) { + // set the display screen size + setScreenSize(width, height, false); + } + + /** + * Create a standard video display buffer with 80 columns and 24 lines. + */ + public VDUBuffer() { + this(80, 24); + } + + /** + * Put a character on the screen with normal font and outline. + * The character previously on that position will be overwritten. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to show on the screen + * @see #insertChar + * @see #deleteChar + * @see #redraw + */ + public void putChar(int c, int l, char ch) { + putChar(c, l, ch, NORMAL); + } + + /** + * Put a character on the screen with specific font and outline. + * The character previously on that position will be overwritten. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to show on the screen + * @param attributes the character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #insertChar + * @see #deleteChar + * @see #redraw + */ + + public void putChar(int c, int l, char ch, int attributes) { + charArray[screenBase + l][c] = ch; + charAttributes[screenBase + l][c] = attributes; + if (l < height) + update[l + 1] = true; + } + + /** + * Get the character at the specified position. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + */ + public char getChar(int c, int l) { + return charArray[screenBase + l][c]; + } + + /** + * Get the attributes for the specified position. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + */ + public int getAttributes(int c, int l) { + return charAttributes[screenBase + l][c]; + } + + /** + * Insert a character at a specific position on the screen. + * All character right to from this position will be moved one to the right. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param ch the character to insert + * @param attributes the character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #deleteChar + * @see #redraw + */ + public void insertChar(int c, int l, char ch, int attributes) { + System.arraycopy(charArray[screenBase + l], c, + charArray[screenBase + l], c + 1, width - c - 1); + System.arraycopy(charAttributes[screenBase + l], c, + charAttributes[screenBase + l], c + 1, width - c - 1); + putChar(c, l, ch, attributes); + } + + /** + * Delete a character at a given position on the screen. + * All characters right to the position will be moved one to the left. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @see #putChar + * @see #insertChar + * @see #redraw + */ + public void deleteChar(int c, int l) { + if (c < width - 1) { + System.arraycopy(charArray[screenBase + l], c + 1, + charArray[screenBase + l], c, width - c - 1); + System.arraycopy(charAttributes[screenBase + l], c + 1, + charAttributes[screenBase + l], c, width - c - 1); + } + putChar(width - 1, l, (char) 0); + } + + /** + * Put a String at a specific position. Any characters previously on that + * position will be overwritten. You need to call redraw() for screen update. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param s the string to be shown on the screen + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #insertLine + * @see #deleteLine + * @see #redraw + */ + public void putString(int c, int l, String s) { + putString(c, l, s, NORMAL); + } + + /** + * Put a String at a specific position giving all characters the same + * attributes. Any characters previously on that position will be + * overwritten. You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (line) + * @param s the string to be shown on the screen + * @param attributes character attributes + * @see #BOLD + * @see #UNDERLINE + * @see #INVERT + * @see #INVISIBLE + * @see #NORMAL + * @see #LOW + * @see #putChar + * @see #insertLine + * @see #deleteLine + * @see #redraw + */ + public void putString(int c, int l, String s, int attributes) { + for (int i = 0; i < s.length() && c + i < width; i++) + putChar(c + i, l, s.charAt(i), attributes); + } + + /** + * Insert a blank line at a specific position. + * The current line and all previous lines are scrolled one line up. The + * top line is lost. You need to call redraw() to update the screen. + * @param l the y-coordinate to insert the line + * @see #deleteLine + * @see #redraw + */ + public void insertLine(int l) { + insertLine(l, 1, SCROLL_UP); + } + + /** + * Insert blank lines at a specific position. + * You need to call redraw() to update the screen + * @param l the y-coordinate to insert the line + * @param n amount of lines to be inserted + * @see #deleteLine + * @see #redraw + */ + public void insertLine(int l, int n) { + insertLine(l, n, SCROLL_UP); + } + + /** + * Insert a blank line at a specific position. Scroll text according to + * the argument. + * You need to call redraw() to update the screen + * @param l the y-coordinate to insert the line + * @param scrollDown scroll down + * @see #deleteLine + * @see #SCROLL_UP + * @see #SCROLL_DOWN + * @see #redraw + */ + public void insertLine(int l, boolean scrollDown) { + insertLine(l, 1, scrollDown); + } + + /** + * Insert blank lines at a specific position. + * The current line and all previous lines are scrolled one line up. The + * top line is lost. You need to call redraw() to update the screen. + * @param l the y-coordinate to insert the line + * @param n number of lines to be inserted + * @param scrollDown scroll down + * @see #deleteLine + * @see #SCROLL_UP + * @see #SCROLL_DOWN + * @see #redraw + */ + public synchronized void insertLine(int l, int n, boolean scrollDown) { + char cbuf[][] = null; + int abuf[][] = null; + int offset = 0; + int oldBase = screenBase; + + int newScreenBase = screenBase; + int newWindowBase = windowBase; + int newBufSize = bufSize; + + if (l > bottomMargin) /* We do not scroll below bottom margin (below the scrolling region). */ + return; + int top = (l < topMargin ? + 0 : (l > bottomMargin ? + (bottomMargin + 1 < height ? + bottomMargin + 1 : height - 1) : topMargin)); + int bottom = (l > bottomMargin ? + height - 1 : (l < topMargin ? + (topMargin > 0 ? + topMargin - 1 : 0) : bottomMargin)); + + // System.out.println("l is "+l+", top is "+top+", bottom is "+bottom+", bottomargin is "+bottomMargin+", topMargin is "+topMargin); + + if (scrollDown) { + if (n > (bottom - top)) n = (bottom - top); + int size = bottom - l - (n - 1); + if(size < 0) size = 0; + cbuf = new char[size][]; + abuf = new int[size][]; + + System.arraycopy(charArray, oldBase + l, cbuf, 0, bottom - l - (n - 1)); + System.arraycopy(charAttributes, oldBase + l, + abuf, 0, bottom - l - (n - 1)); + System.arraycopy(cbuf, 0, charArray, oldBase + l + n, + bottom - l - (n - 1)); + System.arraycopy(abuf, 0, charAttributes, oldBase + l + n, + bottom - l - (n - 1)); + cbuf = charArray; + abuf = charAttributes; + } else { + try { + if (n > (bottom - top) + 1) n = (bottom - top) + 1; + if (bufSize < maxBufSize) { + if (bufSize + n > maxBufSize) { + offset = n - (maxBufSize - bufSize); + scrollMarker += offset; + newBufSize = maxBufSize; + newScreenBase = maxBufSize - height - 1; + newWindowBase = screenBase; + } else { + scrollMarker += n; + newScreenBase += n; + newWindowBase += n; + newBufSize += n; + } + + cbuf = new char[newBufSize][]; + abuf = new int[newBufSize][]; + } else { + offset = n; + cbuf = charArray; + abuf = charAttributes; + } + // copy anything from the top of the buffer (+offset) to the new top + // up to the screenBase. + if (oldBase > 0) { + System.arraycopy(charArray, offset, + cbuf, 0, + oldBase - offset); + System.arraycopy(charAttributes, offset, + abuf, 0, + oldBase - offset); + } + // copy anything from the top of the screen (screenBase) up to the + // topMargin to the new screen + if (top > 0) { + System.arraycopy(charArray, oldBase, + cbuf, newScreenBase, + top); + System.arraycopy(charAttributes, oldBase, + abuf, newScreenBase, + top); + } + // copy anything from the topMargin up to the amount of lines inserted + // to the gap left over between scrollback buffer and screenBase + if (oldBase >= 0) { + System.arraycopy(charArray, oldBase + top, + cbuf, oldBase - offset, + n); + System.arraycopy(charAttributes, oldBase + top, + abuf, oldBase - offset, + n); + } + // copy anything from topMargin + n up to the line linserted to the + // topMargin + System.arraycopy(charArray, oldBase + top + n, + cbuf, newScreenBase + top, + l - top - (n - 1)); + System.arraycopy(charAttributes, oldBase + top + n, + abuf, newScreenBase + top, + l - top - (n - 1)); + // + // copy the all lines next to the inserted to the new buffer + if (l < height - 1) { + System.arraycopy(charArray, oldBase + l + 1, + cbuf, newScreenBase + l + 1, + (height - 1) - l); + System.arraycopy(charAttributes, oldBase + l + 1, + abuf, newScreenBase + l + 1, + (height - 1) - l); + } + } catch (ArrayIndexOutOfBoundsException e) { + // this should not happen anymore, but I will leave the code + // here in case something happens anyway. That code above is + // so complex I always have a hard time understanding what + // I did, even though there are comments + System.err.println("*** Error while scrolling up:"); + System.err.println("--- BEGIN STACK TRACE ---"); + e.printStackTrace(); + System.err.println("--- END STACK TRACE ---"); + System.err.println("bufSize=" + bufSize + ", maxBufSize=" + maxBufSize); + System.err.println("top=" + top + ", bottom=" + bottom); + System.err.println("n=" + n + ", l=" + l); + System.err.println("screenBase=" + screenBase + ", windowBase=" + windowBase); + System.err.println("newScreenBase=" + newScreenBase + ", newWindowBase=" + newWindowBase); + System.err.println("oldBase=" + oldBase); + System.err.println("size.width=" + width + ", size.height=" + height); + System.err.println("abuf.length=" + abuf.length + ", cbuf.length=" + cbuf.length); + System.err.println("*** done dumping debug information"); + } + } + + // this is a little helper to mark the scrolling + scrollMarker -= n; + + + for (int i = 0; i < n; i++) { + cbuf[(newScreenBase + l) + (scrollDown ? i : -i)] = new char[width]; + Arrays.fill(cbuf[(newScreenBase + l) + (scrollDown ? i : -i)], ' '); + abuf[(newScreenBase + l) + (scrollDown ? i : -i)] = new int[width]; + } + + charArray = cbuf; + charAttributes = abuf; + screenBase = newScreenBase; + windowBase = newWindowBase; + bufSize = newBufSize; + + if (scrollDown) + markLine(l, bottom - l + 1); + else + markLine(top, l - top + 1); + + display.updateScrollBar(); + } + + /** + * Delete a line at a specific position. Subsequent lines will be scrolled + * up to fill the space and a blank line is inserted at the end of the + * screen. + * @param l the y-coordinate to insert the line + * @see #deleteLine + */ + public void deleteLine(int l) { + int bottom = (l > bottomMargin ? height - 1: + (l < topMargin?topMargin:bottomMargin + 1)); + int numRows = bottom - l - 1; + + char[] discardedChars = charArray[screenBase + l]; + int[] discardedAttributes = charAttributes[screenBase + l]; + + if (numRows > 0) { + System.arraycopy(charArray, screenBase + l + 1, + charArray, screenBase + l, numRows); + System.arraycopy(charAttributes, screenBase + l + 1, + charAttributes, screenBase + l, numRows); + } + + int newBottomRow = screenBase + bottom - 1; + charArray[newBottomRow] = discardedChars; + charAttributes[newBottomRow] = discardedAttributes; + Arrays.fill(charArray[newBottomRow], ' '); + Arrays.fill(charAttributes[newBottomRow], 0); + + markLine(l, bottom - l); + } + + /** + * Delete a rectangular portion of the screen. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (row) + * @param w with of the area in characters + * @param h height of the area in characters + * @param curAttr attribute to fill + * @see #deleteChar + * @see #deleteLine + * @see #redraw + */ + public void deleteArea(int c, int l, int w, int h, int curAttr) { + int endColumn = c + w; + int targetRow = screenBase + l; + for (int i = 0; i < h && l + i < height; i++) { + Arrays.fill(charAttributes[targetRow], c, endColumn, curAttr); + Arrays.fill(charArray[targetRow], c, endColumn, ' '); + targetRow++; + } + markLine(l, h); + } + + /** + * Delete a rectangular portion of the screen. + * You need to call redraw() to update the screen. + * @param c x-coordinate (column) + * @param l y-coordinate (row) + * @param w with of the area in characters + * @param h height of the area in characters + * @see #deleteChar + * @see #deleteLine + * @see #redraw + */ + public void deleteArea(int c, int l, int w, int h) { + deleteArea(c, l, w, h, 0); + } + + /** + * Sets whether the cursor is visible or not. + * @param doshow + */ + public void showCursor(boolean doshow) { + showcursor = doshow; + } + + /** + * Check whether the cursor is currently visible. + * @return visibility + */ + public boolean isCursorVisible() { + return showcursor; + } + + /** + * Puts the cursor at the specified position. + * @param c column + * @param l line + */ + public void setCursorPosition(int c, int l) { + cursorX = c; + cursorY = l; + } + + /** + * Get the current column of the cursor position. + */ + public int getCursorColumn() { + return cursorX; + } + + /** + * Get the current line of the cursor position. + */ + public int getCursorRow() { + return cursorY; + } + + /** + * Set the current window base. This allows to view the scrollback buffer. + * @param line the line where the screen window starts + * @see #setBufferSize + * @see #getBufferSize + */ + public void setWindowBase(int line) { + if (line > screenBase) + line = screenBase; + else if (line < 0) line = 0; + windowBase = line; + update[0] = true; + redraw(); + } + + /** + * Get the current window base. + * @see #setWindowBase + */ + public int getWindowBase() { + return windowBase; + } + + /** + * Set the scroll margins simultaneously. If they're out of bounds, trim them. + * @param l1 line that is the top + * @param l2 line that is the bottom + */ + public void setMargins(int l1, int l2) { + if (l1 > l2) + return; + + if (l1 < 0) + l1 = 0; + if (l2 >= height) + l2 = height - 1; + + topMargin = l1; + bottomMargin = l2; + } + + /** + * Set the top scroll margin for the screen. If the current bottom margin + * is smaller it will become the top margin and the line will become the + * bottom margin. + * @param l line that is the margin + */ + public void setTopMargin(int l) { + if (l > bottomMargin) { + topMargin = bottomMargin; + bottomMargin = l; + } else + topMargin = l; + if (topMargin < 0) topMargin = 0; + if (bottomMargin >= height) bottomMargin = height - 1; + } + + /** + * Get the top scroll margin. + */ + public int getTopMargin() { + return topMargin; + } + + /** + * Set the bottom scroll margin for the screen. If the current top margin + * is bigger it will become the bottom margin and the line will become the + * top margin. + * @param l line that is the margin + */ + public void setBottomMargin(int l) { + if (l < topMargin) { + bottomMargin = topMargin; + topMargin = l; + } else + bottomMargin = l; + if (topMargin < 0) topMargin = 0; + if (bottomMargin >= height) bottomMargin = height - 1; + } + + /** + * Get the bottom scroll margin. + */ + public int getBottomMargin() { + return bottomMargin; + } + + /** + * Set scrollback buffer size. + * @param amount new size of the buffer + */ + public void setBufferSize(int amount) { + if (amount < height) amount = height; + if (amount < maxBufSize) { + char cbuf[][] = new char[amount][width]; + int abuf[][] = new int[amount][width]; + int copyStart = bufSize - amount < 0 ? 0 : bufSize - amount; + int copyCount = bufSize - amount < 0 ? bufSize : amount; + if (charArray != null) + System.arraycopy(charArray, copyStart, cbuf, 0, copyCount); + if (charAttributes != null) + System.arraycopy(charAttributes, copyStart, abuf, 0, copyCount); + charArray = cbuf; + charAttributes = abuf; + bufSize = copyCount; + screenBase = bufSize - height; + windowBase = screenBase; + } + maxBufSize = amount; + + update[0] = true; + redraw(); + } + + /** + * Retrieve current scrollback buffer size. + * @see #setBufferSize + */ + public int getBufferSize() { + return bufSize; + } + + /** + * Retrieve maximum buffer Size. + * @see #getBufferSize + */ + public int getMaxBufferSize() { + return maxBufSize; + } + + /** + * Change the size of the screen. This will include adjustment of the + * scrollback buffer. + * @param w of the screen + * @param h of the screen + */ + public void setScreenSize(int w, int h, boolean broadcast) { + char cbuf[][]; + int abuf[][]; + int maxSize = bufSize; + + if (w < 1 || h < 1) return; + + if (debug > 0) + System.err.println("VDU: screen size [" + w + "," + h + "]"); + + if (h > maxBufSize) + maxBufSize = h; + + if (h > bufSize) { + bufSize = h; + screenBase = 0; + windowBase = 0; + } + + if (windowBase + h >= bufSize) + windowBase = bufSize - h; + + if (screenBase + h >= bufSize) + screenBase = bufSize - h; + + + cbuf = new char[bufSize][w]; + abuf = new int[bufSize][w]; + + + for (int i = 0; i < bufSize; i++) { + Arrays.fill(cbuf[i], ' '); + } + + if (bufSize < maxSize) + maxSize = bufSize; + + int rowLength; + if (charArray != null && charAttributes != null) { + for (int i = 0; i < maxSize && charArray[i] != null; i++) { + rowLength = charArray[i].length; + System.arraycopy(charArray[i], 0, cbuf[i], 0, + w < rowLength ? w : rowLength); + System.arraycopy(charAttributes[i], 0, abuf[i], 0, + w < rowLength ? w : rowLength); + } + } + + int C = getCursorColumn(); + if (C < 0) + C = 0; + else if (C >= width) + C = width - 1; + + int R = getCursorRow(); + if (R < 0) + R = 0; + else if (R >= height) + R = height - 1; + + setCursorPosition(C, R); + + charArray = cbuf; + charAttributes = abuf; + width = w; + height = h; + topMargin = 0; + bottomMargin = h - 1; + update = new boolean[h + 1]; + update[0] = true; + /* FIXME: ??? + if(resizeStrategy == RESIZE_FONT) + setBounds(getBounds()); + */ + } + + /** + * Get amount of rows on the screen. + */ + public int getRows() { + return height; + } + + /** + * Get amount of columns on the screen. + */ + public int getColumns() { + return width; + } + + /** + * Mark lines to be updated with redraw(). + * @param l starting line + * @param n amount of lines to be updated + * @see #redraw + */ + public void markLine(int l, int n) { + for (int i = 0; (i < n) && (l + i < height); i++) + update[l + i + 1] = true; + } + +// private static int checkBounds(int value, int lower, int upper) { +// if (value < lower) +// return lower; +// else if (value > upper) +// return upper; +// else +// return value; +// } + + /** a generic display that should redraw on demand */ + protected VDUDisplay display; + + public void setDisplay(VDUDisplay display) { + this.display = display; + } + + /** + * Trigger a redraw on the display. + */ + protected void redraw() { + if (display != null) + display.redraw(); + } +} diff --git a/app/src/main/java/de/mud/terminal/VDUDisplay.java b/app/src/main/java/de/mud/terminal/VDUDisplay.java new file mode 100644 index 0000000..bb4a7b8 --- /dev/null +++ b/app/src/main/java/de/mud/terminal/VDUDisplay.java @@ -0,0 +1,40 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +/** + * Generic display + */ +public interface VDUDisplay { + public void redraw(); + public void updateScrollBar(); + + public void setVDUBuffer(VDUBuffer buffer); + public VDUBuffer getVDUBuffer(); + + public void setColor(int index, int red, int green, int blue); + public void resetColors(); +} diff --git a/app/src/main/java/de/mud/terminal/VDUInput.java b/app/src/main/java/de/mud/terminal/VDUInput.java new file mode 100644 index 0000000..43c88de --- /dev/null +++ b/app/src/main/java/de/mud/terminal/VDUInput.java @@ -0,0 +1,90 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meißner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ +package de.mud.terminal; + +import java.util.Properties; + +/** + * An interface for a terminal that accepts input from keyboard and mouse. + * + * @author Matthias L. Jugel, Marcus Meißner + * @version $Id: VDUInput.java 499 2005-09-29 08:24:54Z leo $ + */ +public interface VDUInput { + + public final static int KEY_CONTROL = 0x01; + public final static int KEY_SHIFT = 0x02; + public final static int KEY_ALT = 0x04; + public final static int KEY_ACTION = 0x08; + + + + /** + * Direct access to writing data ... + * @param b + */ + void write(byte b[]); + + /** + * Terminal is mouse-aware and requires (x,y) coordinates of + * on the terminal (character coordinates) and the button clicked. + * @param x + * @param y + * @param modifiers + */ + void mousePressed(int x, int y, int modifiers); + + /** + * Terminal is mouse-aware and requires the coordinates and button + * of the release. + * @param x + * @param y + * @param modifiers + */ + void mouseReleased(int x, int y, int modifiers); + + /** + * Override the standard key codes used by the terminal emulation. + * @param codes a properties object containing key code definitions + */ + void setKeyCodes(Properties codes); + + /** + * main keytyping event handler... + * @param keyCode the key code + * @param keyChar the character represented by the key + * @param modifiers shift/alt/control modifiers + */ + void keyPressed(int keyCode, char keyChar, int modifiers); + + /** + * Handle key Typed events for the terminal, this will get + * all normal key types, but no shift/alt/control/numlock. + * @param keyCode the key code + * @param keyChar the character represented by the key + * @param modifiers shift/alt/control modifiers + */ + void keyTyped(int keyCode, char keyChar, int modifiers); +} diff --git a/app/src/main/java/de/mud/terminal/vt320.java b/app/src/main/java/de/mud/terminal/vt320.java new file mode 100644 index 0000000..73369af --- /dev/null +++ b/app/src/main/java/de/mud/terminal/vt320.java @@ -0,0 +1,3032 @@ +/* + * This file is part of "JTA - Telnet/SSH for the JAVA(tm) platform". + * + * (c) Matthias L. Jugel, Marcus Meiner 1996-2005. All Rights Reserved. + * + * Please visit http://javatelnet.org/ for updates and contact. + * + * --LICENSE NOTICE-- + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * --LICENSE NOTICE-- + * + */ + +package de.mud.terminal; + +import android.text.AndroidCharacter; + +import java.util.Properties; + +/** + * Implementation of a VT terminal emulation plus ANSI compatible. + * <P> + * <B>Maintainer:</B> Marcus Meißner + * + * @version $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $ + * @author Matthias L. Jugel, Marcus Meißner + */ +public abstract class vt320 extends VDUBuffer implements VDUInput { + + /** The current version id tag.<P> + * $Id: vt320.java 507 2005-10-25 10:14:52Z marcus $ + * + */ + public final static String ID = "$Id: vt320.java 507 2005-10-25 10:14:52Z marcus $"; + + /** the debug level */ + private final static int debug = 0; + private StringBuilder debugStr; + public abstract void debug(String notice); + + /** + * Write an answer back to the remote host. This is needed to be able to + * send terminal answers requests like status and type information. + * @param b the array of bytes to be sent + */ + public abstract void write(byte[] b); + + /** + * Write an answer back to the remote host. This is needed to be able to + * send terminal answers requests like status and type information. + * @param b the array of bytes to be sent + */ + public abstract void write(int b); + + /** + * Play the beep sound ... + */ + public void beep() { /* do nothing by default */ + } + + /** + * Convenience function for putString(char[], int, int) + */ + public void putString(String s) { + int len = s.length(); + char[] tmp = new char[len]; + s.getChars(0, len, tmp, 0); + putString(tmp, null, 0, len); + } + + /** + * Put string at current cursor position. Moves cursor + * according to the String. Does NOT wrap. + * @param s character array + * @param start place to start in array + * @param len number of characters to process + */ + public void putString(char[] s, byte[] fullwidths, int start, int len) { + if (len > 0) { + //markLine(R, 1); + int lastChar = -1; + char c; + boolean isWide = false; + + for (int i = 0; i < len; i++) { + c = s[start + i]; + // Shortcut for my favorite ASCII + if (c <= 0x7F) { + if (lastChar != -1) + putChar((char) lastChar, isWide, false); + lastChar = c; + isWide = false; + } else if (!Character.isLowSurrogate(c) && !Character.isHighSurrogate(c)) { + if (Character.getType(c) == Character.NON_SPACING_MARK) { + if (lastChar != -1) { + char nc = Precomposer.precompose((char) lastChar, c); + putChar(nc, isWide, false); + lastChar = -1; + } + } else { + if (lastChar != -1) + putChar((char) lastChar, isWide, false); + lastChar = c; + if (fullwidths != null) { + final byte width = fullwidths[i]; + isWide = (width == AndroidCharacter.EAST_ASIAN_WIDTH_WIDE) + || (width == AndroidCharacter.EAST_ASIAN_WIDTH_FULL_WIDTH); + } + } + } + } + + if (lastChar != -1) + putChar((char) lastChar, isWide, false); + + setCursorPosition(C, R); + redraw(); + } + } + + protected void sendTelnetCommand(byte cmd) { + + } + + /** + * Sent the changed window size from the terminal to all listeners. + */ + protected void setWindowSize(int c, int r) { + /* To be overridden by Terminal.java */ + } + + @Override +public void setScreenSize(int c, int r, boolean broadcast) { + int oldrows = height; + + if (debug>2) { + if (debugStr == null) + debugStr = new StringBuilder(); + + debugStr.append("setscreensize (") + .append(c) + .append(',') + .append(r) + .append(',') + .append(broadcast) + .append(')'); + debug(debugStr.toString()); + debugStr.setLength(0); + } + + super.setScreenSize(c,r,false); + + boolean cursorChanged = false; + + // Don't let the cursor go off the screen. + if (C >= c) { + C = c - 1; + cursorChanged = true; + } + + if (R >= r) { + R = r - 1; + cursorChanged = true; + } + + if (cursorChanged) { + setCursorPosition(C, R); + redraw(); + } + + if (broadcast) { + setWindowSize(c, r); /* broadcast up */ + } + } + + + /** + * Create a new vt320 terminal and intialize it with useful settings. + */ + public vt320(int width, int height) { + super(width, height); + + debugStr = new StringBuilder(); + + setVMS(false); + setIBMCharset(false); + setTerminalID("vt320"); + setBufferSize(100); + //setBorder(2, false); + + gx = new char[4]; + reset(); + + /* top row of numpad */ + PF1 = "\u001bOP"; + PF2 = "\u001bOQ"; + PF3 = "\u001bOR"; + PF4 = "\u001bOS"; + + /* the 3x2 keyblock on PC keyboards */ + Insert = new String[4]; + Remove = new String[4]; + KeyHome = new String[4]; + KeyEnd = new String[4]; + NextScn = new String[4]; + PrevScn = new String[4]; + Escape = new String[4]; + BackSpace = new String[4]; + TabKey = new String[4]; + Insert[0] = Insert[1] = Insert[2] = Insert[3] = "\u001b[2~"; + Remove[0] = Remove[1] = Remove[2] = Remove[3] = "\u001b[3~"; + PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[5~"; + NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[6~"; + KeyHome[0] = KeyHome[1] = KeyHome[2] = KeyHome[3] = "\u001b[H"; + KeyEnd[0] = KeyEnd[1] = KeyEnd[2] = KeyEnd[3] = "\u001b[F"; + Escape[0] = Escape[1] = Escape[2] = Escape[3] = "\u001b"; + if (vms) { + BackSpace[1] = "" + (char) 10; // VMS shift deletes word back + BackSpace[2] = "\u0018"; // VMS control deletes line back + BackSpace[0] = BackSpace[3] = "\u007f"; // VMS other is delete + } else { + //BackSpace[0] = BackSpace[1] = BackSpace[2] = BackSpace[3] = "\b"; + // ConnectBot modifications. + BackSpace[0] = "\b"; + BackSpace[1] = "\u007f"; + BackSpace[2] = "\u001b[3~"; + BackSpace[3] = "\u001b[2~"; + } + + /* some more VT100 keys */ + Find = "\u001b[1~"; + Select = "\u001b[4~"; + Help = "\u001b[28~"; + Do = "\u001b[29~"; + + FunctionKey = new String[21]; + FunctionKey[0] = ""; + FunctionKey[1] = PF1; + FunctionKey[2] = PF2; + FunctionKey[3] = PF3; + FunctionKey[4] = PF4; + /* following are defined differently for vt220 / vt132 ... */ + FunctionKey[5] = "\u001b[15~"; + FunctionKey[6] = "\u001b[17~"; + FunctionKey[7] = "\u001b[18~"; + FunctionKey[8] = "\u001b[19~"; + FunctionKey[9] = "\u001b[20~"; + FunctionKey[10] = "\u001b[21~"; + FunctionKey[11] = "\u001b[23~"; + FunctionKey[12] = "\u001b[24~"; + FunctionKey[13] = "\u001b[25~"; + FunctionKey[14] = "\u001b[26~"; + FunctionKey[15] = Help; + FunctionKey[16] = Do; + FunctionKey[17] = "\u001b[31~"; + FunctionKey[18] = "\u001b[32~"; + FunctionKey[19] = "\u001b[33~"; + FunctionKey[20] = "\u001b[34~"; + + FunctionKeyShift = new String[21]; + FunctionKeyAlt = new String[21]; + FunctionKeyCtrl = new String[21]; + + for (int i = 0; i < 20; i++) { + FunctionKeyShift[i] = ""; + FunctionKeyAlt[i] = ""; + FunctionKeyCtrl[i] = ""; + } + FunctionKeyShift[15] = Find; + FunctionKeyShift[16] = Select; + + + TabKey[0] = "\u0009"; + TabKey[1] = "\u001bOP\u0009"; + TabKey[2] = TabKey[3] = ""; + + KeyUp = new String[4]; + KeyUp[0] = "\u001b[A"; + KeyDown = new String[4]; + KeyDown[0] = "\u001b[B"; + KeyRight = new String[4]; + KeyRight[0] = "\u001b[C"; + KeyLeft = new String[4]; + KeyLeft[0] = "\u001b[D"; + Numpad = new String[10]; + Numpad[0] = "\u001bOp"; + Numpad[1] = "\u001bOq"; + Numpad[2] = "\u001bOr"; + Numpad[3] = "\u001bOs"; + Numpad[4] = "\u001bOt"; + Numpad[5] = "\u001bOu"; + Numpad[6] = "\u001bOv"; + Numpad[7] = "\u001bOw"; + Numpad[8] = "\u001bOx"; + Numpad[9] = "\u001bOy"; + KPMinus = PF4; + KPComma = "\u001bOl"; + KPPeriod = "\u001bOn"; + KPEnter = "\u001bOM"; + + NUMPlus = new String[4]; + NUMPlus[0] = "+"; + NUMDot = new String[4]; + NUMDot[0] = "."; + } + + public void setBackspace(int type) { + switch (type) { + case DELETE_IS_DEL: + BackSpace[0] = "\u007f"; + BackSpace[1] = "\b"; + break; + case DELETE_IS_BACKSPACE: + BackSpace[0] = "\b"; + BackSpace[1] = "\u007f"; + break; + } + } + + /** + * Create a default vt320 terminal with 80 columns and 24 lines. + */ + public vt320() { + this(80, 24); + } + + /** + * Terminal is mouse-aware and requires (x,y) coordinates of + * on the terminal (character coordinates) and the button clicked. + * @param x + * @param y + * @param modifiers + */ + public void mousePressed(int x, int y, int modifiers) { + if (mouserpt == 0) + return; + + int mods = modifiers; + mousebut = 3; + if ((mods & 16) == 16) mousebut = 0; + if ((mods & 8) == 8) mousebut = 1; + if ((mods & 4) == 4) mousebut = 2; + + int mousecode; + if (mouserpt == 9) /* X10 Mouse */ + mousecode = 0x20 | mousebut; + else /* normal xterm mouse reporting */ + mousecode = mousebut | 0x20 | ((mods & 7) << 2); + + byte b[] = new byte[6]; + + b[0] = 27; + b[1] = (byte) '['; + b[2] = (byte) 'M'; + b[3] = (byte) mousecode; + b[4] = (byte) (0x20 + x + 1); + b[5] = (byte) (0x20 + y + 1); + + write(b); // FIXME: writeSpecial here + } + + /** + * Terminal is mouse-aware and requires the coordinates and button + * of the release. + * @param x + * @param y + * @param modifiers + */ + public void mouseReleased(int x, int y, int modifiers) { + if (mouserpt == 0) + return; + + /* problem is tht modifiers still have the released button set in them. + int mods = modifiers; + mousebut = 3; + if ((mods & 16)==16) mousebut=0; + if ((mods & 8)==8 ) mousebut=1; + if ((mods & 4)==4 ) mousebut=2; + */ + + int mousecode; + if (mouserpt == 9) + mousecode = 0x20 + mousebut; /* same as press? appears so. */ + else + mousecode = '#'; + + byte b[] = new byte[6]; + b[0] = 27; + b[1] = (byte) '['; + b[2] = (byte) 'M'; + b[3] = (byte) mousecode; + b[4] = (byte) (0x20 + x + 1); + b[5] = (byte) (0x20 + y + 1); + write(b); // FIXME: writeSpecial here + mousebut = 0; + } + + + /** we should do localecho (passed from other modules). false is default */ + private boolean localecho = false; + + /** + * Enable or disable the local echo property of the terminal. + * @param echo true if the terminal should echo locally + */ + public void setLocalEcho(boolean echo) { + localecho = echo; + } + + /** + * Enable the VMS mode of the terminal to handle some things differently + * for VMS hosts. + * @param vms true for vms mode, false for normal mode + */ + public void setVMS(boolean vms) { + this.vms = vms; + } + + /** + * Enable the usage of the IBM character set used by some BBS's. Special + * graphical character are available in this mode. + * @param ibm true to use the ibm character set + */ + public void setIBMCharset(boolean ibm) { + useibmcharset = ibm; + } + + /** + * Override the standard key codes used by the terminal emulation. + * @param codes a properties object containing key code definitions + */ + public void setKeyCodes(Properties codes) { + String res, prefixes[] = {"", "S", "C", "A"}; + int i; + + for (i = 0; i < 10; i++) { + res = codes.getProperty("NUMPAD" + i); + if (res != null) Numpad[i] = unEscape(res); + } + for (i = 1; i < 20; i++) { + res = codes.getProperty("F" + i); + if (res != null) FunctionKey[i] = unEscape(res); + res = codes.getProperty("SF" + i); + if (res != null) FunctionKeyShift[i] = unEscape(res); + res = codes.getProperty("CF" + i); + if (res != null) FunctionKeyCtrl[i] = unEscape(res); + res = codes.getProperty("AF" + i); + if (res != null) FunctionKeyAlt[i] = unEscape(res); + } + for (i = 0; i < 4; i++) { + res = codes.getProperty(prefixes[i] + "PGUP"); + if (res != null) PrevScn[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "PGDOWN"); + if (res != null) NextScn[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "END"); + if (res != null) KeyEnd[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "HOME"); + if (res != null) KeyHome[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "INSERT"); + if (res != null) Insert[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "REMOVE"); + if (res != null) Remove[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "UP"); + if (res != null) KeyUp[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "DOWN"); + if (res != null) KeyDown[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "LEFT"); + if (res != null) KeyLeft[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "RIGHT"); + if (res != null) KeyRight[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "ESCAPE"); + if (res != null) Escape[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "BACKSPACE"); + if (res != null) BackSpace[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "TAB"); + if (res != null) TabKey[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "NUMPLUS"); + if (res != null) NUMPlus[i] = unEscape(res); + res = codes.getProperty(prefixes[i] + "NUMDECIMAL"); + if (res != null) NUMDot[i] = unEscape(res); + } + } + + /** + * Set the terminal id used to identify this terminal. + * @param terminalID the id string + */ + public void setTerminalID(String terminalID) { + this.terminalID = terminalID; + + if (terminalID.equals("scoansi")) { + FunctionKey[1] = "\u001b[M"; FunctionKey[2] = "\u001b[N"; + FunctionKey[3] = "\u001b[O"; FunctionKey[4] = "\u001b[P"; + FunctionKey[5] = "\u001b[Q"; FunctionKey[6] = "\u001b[R"; + FunctionKey[7] = "\u001b[S"; FunctionKey[8] = "\u001b[T"; + FunctionKey[9] = "\u001b[U"; FunctionKey[10] = "\u001b[V"; + FunctionKey[11] = "\u001b[W"; FunctionKey[12] = "\u001b[X"; + FunctionKey[13] = "\u001b[Y"; FunctionKey[14] = "?"; + FunctionKey[15] = "\u001b[a"; FunctionKey[16] = "\u001b[b"; + FunctionKey[17] = "\u001b[c"; FunctionKey[18] = "\u001b[d"; + FunctionKey[19] = "\u001b[e"; FunctionKey[20] = "\u001b[f"; + PrevScn[0] = PrevScn[1] = PrevScn[2] = PrevScn[3] = "\u001b[I"; + NextScn[0] = NextScn[1] = NextScn[2] = NextScn[3] = "\u001b[G"; + // more theoretically. + } + } + + public void setAnswerBack(String ab) { + this.answerBack = unEscape(ab); + } + + /** + * Get the terminal id used to identify this terminal. + */ + public String getTerminalID() { + return terminalID; + } + + /** + * A small conveniance method thar converts the string to a byte array + * for sending. + * @param s the string to be sent + */ + private boolean write(String s, boolean doecho) { + if (debug > 2) { + debugStr.append("write(|") + .append(s) + .append("|,") + .append(doecho); + debug(debugStr.toString()); + debugStr.setLength(0); + } + if (s == null) // aka the empty string. + return true; + /* NOTE: getBytes() honours some locale, it *CONVERTS* the string. + * However, we output only 7bit stuff towards the target, and *some* + * 8 bit control codes. We must not mess up the latter, so we do hand + * by hand copy. + */ + + byte arr[] = new byte[s.length()]; + for (int i = 0; i < s.length(); i++) { + arr[i] = (byte) s.charAt(i); + } + write(arr); + + if (doecho) + putString(s); + return true; + } + + private boolean write(int s, boolean doecho) { + if (debug > 2) { + debugStr.append("write(|") + .append(s) + .append("|,") + .append(doecho); + debug(debugStr.toString()); + debugStr.setLength(0); + } + + write(s); + + // TODO check if character is wide + if (doecho) + putChar((char)s, false, false); + return true; + } + + private boolean write(String s) { + return write(s, localecho); + } + + // =================================================================== + // the actual terminal emulation code comes here: + // =================================================================== + + private String terminalID = "vt320"; + private String answerBack = "Use Terminal.answerback to set ...\n"; + + // X - COLUMNS, Y - ROWS + int R,C; + int attributes = 0; + + int Sc,Sr,Sa,Stm,Sbm; + char Sgr,Sgl; + char Sgx[]; + + int insertmode = 0; + int statusmode = 0; + boolean vt52mode = false; + boolean keypadmode = false; /* false - numeric, true - application */ + boolean output8bit = false; + int normalcursor = 0; + boolean moveoutsidemargins = true; + boolean wraparound = true; + boolean sendcrlf = true; + boolean capslock = false; + boolean numlock = false; + int mouserpt = 0; + byte mousebut = 0; + + boolean useibmcharset = false; + + int lastwaslf = 0; + boolean usedcharsets = false; + + private final static char ESC = 27; + private final static char IND = 132; + private final static char NEL = 133; + private final static char RI = 141; + private final static char SS2 = 142; + private final static char SS3 = 143; + private final static char DCS = 144; + private final static char HTS = 136; + private final static char CSI = 155; + private final static char OSC = 157; + private final static int TSTATE_DATA = 0; + private final static int TSTATE_ESC = 1; /* ESC */ + private final static int TSTATE_CSI = 2; /* ESC [ */ + private final static int TSTATE_DCS = 3; /* ESC P */ + private final static int TSTATE_DCEQ = 4; /* ESC [? */ + private final static int TSTATE_ESCSQUARE = 5; /* ESC # */ + private final static int TSTATE_OSC = 6; /* ESC ] */ + private final static int TSTATE_SETG0 = 7; /* ESC (? */ + private final static int TSTATE_SETG1 = 8; /* ESC )? */ + private final static int TSTATE_SETG2 = 9; /* ESC *? */ + private final static int TSTATE_SETG3 = 10; /* ESC +? */ + private final static int TSTATE_CSI_DOLLAR = 11; /* ESC [ Pn $ */ + private final static int TSTATE_CSI_EX = 12; /* ESC [ ! */ + private final static int TSTATE_ESCSPACE = 13; /* ESC <space> */ + private final static int TSTATE_VT52X = 14; + private final static int TSTATE_VT52Y = 15; + private final static int TSTATE_CSI_TICKS = 16; + private final static int TSTATE_CSI_EQUAL = 17; /* ESC [ = */ + private final static int TSTATE_TITLE = 18; /* xterm title */ + + /* Keys we support */ + public final static int KEY_PAUSE = 1; + public final static int KEY_F1 = 2; + public final static int KEY_F2 = 3; + public final static int KEY_F3 = 4; + public final static int KEY_F4 = 5; + public final static int KEY_F5 = 6; + public final static int KEY_F6 = 7; + public final static int KEY_F7 = 8; + public final static int KEY_F8 = 9; + public final static int KEY_F9 = 10; + public final static int KEY_F10 = 11; + public final static int KEY_F11 = 12; + public final static int KEY_F12 = 13; + public final static int KEY_UP = 14; + public final static int KEY_DOWN =15 ; + public final static int KEY_LEFT = 16; + public final static int KEY_RIGHT = 17; + public final static int KEY_PAGE_DOWN = 18; + public final static int KEY_PAGE_UP = 19; + public final static int KEY_INSERT = 20; + public final static int KEY_DELETE = 21; + public final static int KEY_BACK_SPACE = 22; + public final static int KEY_HOME = 23; + public final static int KEY_END = 24; + public final static int KEY_NUM_LOCK = 25; + public final static int KEY_CAPS_LOCK = 26; + public final static int KEY_SHIFT = 27; + public final static int KEY_CONTROL = 28; + public final static int KEY_ALT = 29; + public final static int KEY_ENTER = 30; + public final static int KEY_NUMPAD0 = 31; + public final static int KEY_NUMPAD1 = 32; + public final static int KEY_NUMPAD2 = 33; + public final static int KEY_NUMPAD3 = 34; + public final static int KEY_NUMPAD4 = 35; + public final static int KEY_NUMPAD5 = 36; + public final static int KEY_NUMPAD6 = 37; + public final static int KEY_NUMPAD7 = 38; + public final static int KEY_NUMPAD8 = 39; + public final static int KEY_NUMPAD9 = 40; + public final static int KEY_DECIMAL = 41; + public final static int KEY_ADD = 42; + public final static int KEY_ESCAPE = 43; + + public final static int DELETE_IS_DEL = 0; + public final static int DELETE_IS_BACKSPACE = 1; + + /* The graphics charsets + * B - default ASCII + * A - ISO Latin 1 + * 0 - DEC SPECIAL + * < - User defined + * .... + */ + char gx[]; + char gl; // GL (left charset) + char gr; // GR (right charset) + int onegl; // single shift override for GL. + + // Map from scoansi linedrawing to DEC _and_ unicode (for the stuff which + // is not in linedrawing). Got from experimenting with scoadmin. + private final static String scoansi_acs = "Tm7k3x4u?kZl@mYjEnB\u2566DqCtAvM\u2550:\u2551N\u2557I\u2554;\u2557H\u255a0a<\u255d"; + // array to store DEC Special -> Unicode mapping + // Unicode DEC Unicode name (DEC name) + private static char DECSPECIAL[] = { + '\u0040', //5f blank + '\u2666', //60 black diamond + '\u2592', //61 grey square + '\u2409', //62 Horizontal tab (ht) pict. for control + '\u240c', //63 Form Feed (ff) pict. for control + '\u240d', //64 Carriage Return (cr) pict. for control + '\u240a', //65 Line Feed (lf) pict. for control + '\u00ba', //66 Masculine ordinal indicator + '\u00b1', //67 Plus or minus sign + '\u2424', //68 New Line (nl) pict. for control + '\u240b', //69 Vertical Tab (vt) pict. for control + '\u2518', //6a Forms light up and left + '\u2510', //6b Forms light down and left + '\u250c', //6c Forms light down and right + '\u2514', //6d Forms light up and right + '\u253c', //6e Forms light vertical and horizontal + '\u2594', //6f Upper 1/8 block (Scan 1) + '\u2580', //70 Upper 1/2 block (Scan 3) + '\u2500', //71 Forms light horizontal or ?em dash? (Scan 5) + '\u25ac', //72 \u25ac black rect. or \u2582 lower 1/4 (Scan 7) + '\u005f', //73 \u005f underscore or \u2581 lower 1/8 (Scan 9) + '\u251c', //74 Forms light vertical and right + '\u2524', //75 Forms light vertical and left + '\u2534', //76 Forms light up and horizontal + '\u252c', //77 Forms light down and horizontal + '\u2502', //78 vertical bar + '\u2264', //79 less than or equal + '\u2265', //7a greater than or equal + '\u00b6', //7b paragraph + '\u2260', //7c not equal + '\u00a3', //7d Pound Sign (british) + '\u00b7' //7e Middle Dot + }; + + /** Strings to send on function key pressing */ + private String Numpad[]; + private String FunctionKey[]; + private String FunctionKeyShift[]; + private String FunctionKeyCtrl[]; + private String FunctionKeyAlt[]; + private String TabKey[]; + private String KeyUp[],KeyDown[],KeyLeft[],KeyRight[]; + private String KPMinus, KPComma, KPPeriod, KPEnter; + private String PF1, PF2, PF3, PF4; + private String Help, Do, Find, Select; + + private String KeyHome[], KeyEnd[], Insert[], Remove[], PrevScn[], NextScn[]; + private String Escape[], BackSpace[], NUMDot[], NUMPlus[]; + + private String osc,dcs; /* to memorize OSC & DCS control sequence */ + + /** vt320 state variable (internal) */ + private int term_state = TSTATE_DATA; + /** in vms mode, set by Terminal.VMS property */ + private boolean vms = false; + /** Tabulators */ + private byte[] Tabs; + /** The list of integers as used by CSI */ + private int[] DCEvars = new int[30]; + private int DCEvar; + + /** + * Replace escape code characters (backslash + identifier) with their + * respective codes. + * @param tmp the string to be parsed + * @return a unescaped string + */ + static String unEscape(String tmp) { + int idx = 0, oldidx = 0; + String cmd; + // f.println("unescape("+tmp+")"); + cmd = ""; + while ((idx = tmp.indexOf('\\', oldidx)) >= 0 && + ++idx <= tmp.length()) { + cmd += tmp.substring(oldidx, idx - 1); + if (idx == tmp.length()) return cmd; + switch (tmp.charAt(idx)) { + case 'b': + cmd += "\b"; + break; + case 'e': + cmd += "\u001b"; + break; + case 'n': + cmd += "\n"; + break; + case 'r': + cmd += "\r"; + break; + case 't': + cmd += "\t"; + break; + case 'v': + cmd += "\u000b"; + break; + case 'a': + cmd += "\u0012"; + break; + default : + if ((tmp.charAt(idx) >= '0') && (tmp.charAt(idx) <= '9')) { + int i; + for (i = idx; i < tmp.length(); i++) + if ((tmp.charAt(i) < '0') || (tmp.charAt(i) > '9')) + break; + cmd += (char) Integer.parseInt(tmp.substring(idx, i)); + idx = i - 1; + } else + cmd += tmp.substring(idx, ++idx); + break; + } + oldidx = ++idx; + } + if (oldidx <= tmp.length()) cmd += tmp.substring(oldidx); + return cmd; + } + + /** + * A small conveniance method thar converts a 7bit string to the 8bit + * version depending on VT52/Output8Bit mode. + * + * @param s the string to be sent + */ + private boolean writeSpecial(String s) { + if (s == null) + return true; + if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == 'O'))) { + if (vt52mode) { + if ((s.charAt(2) >= 'P') && (s.charAt(2) <= 'S')) { + s = "\u001b" + s.substring(2); /* ESC x */ + } else { + s = "\u001b?" + s.substring(2); /* ESC ? x */ + } + } else { + if (output8bit) { + s = "\u008f" + s.substring(2); /* SS3 x */ + } /* else keep string as it is */ + } + } + if (((s.length() >= 3) && (s.charAt(0) == 27) && (s.charAt(1) == '['))) { + if (output8bit) { + s = "\u009b" + s.substring(2); /* CSI ... */ + } /* else keep */ + } + return write(s, false); + } + + /** + * main keytyping event handler... + */ + public void keyPressed(int keyCode, char keyChar, int modifiers) { + boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0; + boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0; + boolean alt = (modifiers & VDUInput.KEY_ALT) != 0; + + if (debug > 1) { + debugStr.append("keyPressed(") + .append(keyCode) + .append(", ") + .append((int)keyChar) + .append(", ") + .append(modifiers) + .append(')'); + debug(debugStr.toString()); + debugStr.setLength(0); + } + + int xind; + String fmap[]; + xind = 0; + fmap = FunctionKey; + if (shift) { + fmap = FunctionKeyShift; + xind = 1; + } + if (control) { + fmap = FunctionKeyCtrl; + xind = 2; + } + if (alt) { + fmap = FunctionKeyAlt; + xind = 3; + } + + switch (keyCode) { + case KEY_PAUSE: + if (shift || control) + sendTelnetCommand((byte) 243); // BREAK + break; + case KEY_F1: + writeSpecial(fmap[1]); + break; + case KEY_F2: + writeSpecial(fmap[2]); + break; + case KEY_F3: + writeSpecial(fmap[3]); + break; + case KEY_F4: + writeSpecial(fmap[4]); + break; + case KEY_F5: + writeSpecial(fmap[5]); + break; + case KEY_F6: + writeSpecial(fmap[6]); + break; + case KEY_F7: + writeSpecial(fmap[7]); + break; + case KEY_F8: + writeSpecial(fmap[8]); + break; + case KEY_F9: + writeSpecial(fmap[9]); + break; + case KEY_F10: + writeSpecial(fmap[10]); + break; + case KEY_F11: + writeSpecial(fmap[11]); + break; + case KEY_F12: + writeSpecial(fmap[12]); + break; + case KEY_UP: + writeSpecial(KeyUp[xind]); + break; + case KEY_DOWN: + writeSpecial(KeyDown[xind]); + break; + case KEY_LEFT: + writeSpecial(KeyLeft[xind]); + break; + case KEY_RIGHT: + writeSpecial(KeyRight[xind]); + break; + case KEY_PAGE_DOWN: + writeSpecial(NextScn[xind]); + break; + case KEY_PAGE_UP: + writeSpecial(PrevScn[xind]); + break; + case KEY_INSERT: + writeSpecial(Insert[xind]); + break; + case KEY_DELETE: + writeSpecial(Remove[xind]); + break; + case KEY_BACK_SPACE: + writeSpecial(BackSpace[xind]); + if (localecho) { + if (BackSpace[xind] == "\b") { + putString("\b \b"); // make the last char 'deleted' + } else { + putString(BackSpace[xind]); // echo it + } + } + break; + case KEY_HOME: + writeSpecial(KeyHome[xind]); + break; + case KEY_END: + writeSpecial(KeyEnd[xind]); + break; + case KEY_NUM_LOCK: + if (vms && control) { + writeSpecial(PF1); + } + if (!control) + numlock = !numlock; + break; + case KEY_CAPS_LOCK: + capslock = !capslock; + return; + case KEY_SHIFT: + case KEY_CONTROL: + case KEY_ALT: + return; + default: + break; + } + } +/* + public void keyReleased(KeyEvent evt) { + if (debug > 1) debug("keyReleased("+evt+")"); + // ignore + } +*/ + /** + * Handle key Typed events for the terminal, this will get + * all normal key types, but no shift/alt/control/numlock. + */ + public void keyTyped(int keyCode, char keyChar, int modifiers) { + boolean control = (modifiers & VDUInput.KEY_CONTROL) != 0; + boolean shift = (modifiers & VDUInput.KEY_SHIFT) != 0; + boolean alt = (modifiers & VDUInput.KEY_ALT) != 0; + + if (debug > 1) debug("keyTyped("+keyCode+", "+(int)keyChar+", "+modifiers+")"); + + if (keyChar == '\t') { + if (shift) { + write(TabKey[1], false); + } else { + if (control) { + write(TabKey[2], false); + } else { + if (alt) { + write(TabKey[3], false); + } else { + write(TabKey[0], false); + } + } + } + return; + } + if (alt) { + write(((char) (keyChar | 0x80))); + return; + } + + if (((keyCode == KEY_ENTER) || (keyChar == 10)) + && !control) { + write('\r'); + if (localecho) putString("\r\n"); // bad hack + return; + } + + if ((keyCode == 10) && !control) { + debug("Sending \\r"); + write('\r'); + return; + } + + // FIXME: on german PC keyboards you have to use Alt-Ctrl-q to get an @, + // so we can't just use it here... will probably break some other VMS + // codes. -Marcus + // if(((!vms && keyChar == '2') || keyChar == '@' || keyChar == ' ') + // && control) + if (((!vms && keyChar == '2') || keyChar == ' ') && control) + write(0); + + if (vms) { + if (keyChar == 127 && !control) { + if (shift) + writeSpecial(Insert[0]); // VMS shift delete = insert + else + writeSpecial(Remove[0]); // VMS delete = remove + return; + } else if (control) + switch (keyChar) { + case '0': + writeSpecial(Numpad[0]); + return; + case '1': + writeSpecial(Numpad[1]); + return; + case '2': + writeSpecial(Numpad[2]); + return; + case '3': + writeSpecial(Numpad[3]); + return; + case '4': + writeSpecial(Numpad[4]); + return; + case '5': + writeSpecial(Numpad[5]); + return; + case '6': + writeSpecial(Numpad[6]); + return; + case '7': + writeSpecial(Numpad[7]); + return; + case '8': + writeSpecial(Numpad[8]); + return; + case '9': + writeSpecial(Numpad[9]); + return; + case '.': + writeSpecial(KPPeriod); + return; + case '-': + case 31: + writeSpecial(KPMinus); + return; + case '+': + writeSpecial(KPComma); + return; + case 10: + writeSpecial(KPEnter); + return; + case '/': + writeSpecial(PF2); + return; + case '*': + writeSpecial(PF3); + return; + /* NUMLOCK handled in keyPressed */ + default: + break; + } + /* Now what does this do and how did it get here. -Marcus + if (shift && keyChar < 32) { + write(PF1+(char)(keyChar + 64)); + return; + } + */ + } + + // FIXME: not used? + //String fmap[]; + int xind; + xind = 0; + //fmap = FunctionKey; + if (shift) { + //fmap = FunctionKeyShift; + xind = 1; + } + if (control) { + //fmap = FunctionKeyCtrl; + xind = 2; + } + if (alt) { + //fmap = FunctionKeyAlt; + xind = 3; + } + + if (keyCode == KEY_ESCAPE) { + writeSpecial(Escape[xind]); + return; + } + + if ((modifiers & VDUInput.KEY_ACTION) != 0) + switch (keyCode) { + case KEY_NUMPAD0: + writeSpecial(Numpad[0]); + return; + case KEY_NUMPAD1: + writeSpecial(Numpad[1]); + return; + case KEY_NUMPAD2: + writeSpecial(Numpad[2]); + return; + case KEY_NUMPAD3: + writeSpecial(Numpad[3]); + return; + case KEY_NUMPAD4: + writeSpecial(Numpad[4]); + return; + case KEY_NUMPAD5: + writeSpecial(Numpad[5]); + return; + case KEY_NUMPAD6: + writeSpecial(Numpad[6]); + return; + case KEY_NUMPAD7: + writeSpecial(Numpad[7]); + return; + case KEY_NUMPAD8: + writeSpecial(Numpad[8]); + return; + case KEY_NUMPAD9: + writeSpecial(Numpad[9]); + return; + case KEY_DECIMAL: + writeSpecial(NUMDot[xind]); + return; + case KEY_ADD: + writeSpecial(NUMPlus[xind]); + return; + } + + if (!((keyChar == 8) || (keyChar == 127) || (keyChar == '\r') || (keyChar == '\n'))) { + write(keyChar); + return; + } + } + + private void handle_dcs(String dcs) { + debugStr.append("DCS: ") + .append(dcs); + debug(debugStr.toString()); + debugStr.setLength(0); + } + + private void handle_osc(String osc) { + if (osc.length() > 2 && osc.substring(0, 2).equals("4;")) { + // Define color palette + String[] colorData = osc.split(";"); + + try { + int colorIndex = Integer.parseInt(colorData[1]); + + if ("rgb:".equals(colorData[2].substring(0, 4))) { + String[] rgb = colorData[2].substring(4).split("/"); + + int red = Integer.parseInt(rgb[0].substring(0, 2), 16) & 0xFF; + int green = Integer.parseInt(rgb[1].substring(0, 2), 16) & 0xFF; + int blue = Integer.parseInt(rgb[2].substring(0, 2), 16) & 0xFF; + display.setColor(colorIndex, red, green, blue); + } + } catch (Exception e) { + debugStr.append("OSC: invalid color sequence encountered: ") + .append(osc); + debug(debugStr.toString()); + debugStr.setLength(0); + } + } else + debug("OSC: " + osc); + } + + private final static char unimap[] = { + //# + //# Name: cp437_DOSLatinUS to Unicode table + //# Unicode version: 1.1 + //# Table version: 1.1 + //# Table format: Format A + //# Date: 03/31/95 + //# Authors: Michel Suignard <michelsu@microsoft.com> + //# Lori Hoerth <lorih@microsoft.com> + //# General notes: none + //# + //# Format: Three tab-separated columns + //# Column #1 is the cp1255_WinHebrew code (in hex) + //# Column #2 is the Unicode (in hex as 0xXXXX) + //# Column #3 is the Unicode name (follows a comment sign, '#') + //# + //# The entries are in cp437_DOSLatinUS order + //# + + 0x0000, // #NULL + 0x0001, // #START OF HEADING + 0x0002, // #START OF TEXT + 0x0003, // #END OF TEXT + 0x0004, // #END OF TRANSMISSION + 0x0005, // #ENQUIRY + 0x0006, // #ACKNOWLEDGE + 0x0007, // #BELL + 0x0008, // #BACKSPACE + 0x0009, // #HORIZONTAL TABULATION + 0x000a, // #LINE FEED + 0x000b, // #VERTICAL TABULATION + 0x000c, // #FORM FEED + 0x000d, // #CARRIAGE RETURN + 0x000e, // #SHIFT OUT + 0x000f, // #SHIFT IN + 0x0010, // #DATA LINK ESCAPE + 0x0011, // #DEVICE CONTROL ONE + 0x0012, // #DEVICE CONTROL TWO + 0x0013, // #DEVICE CONTROL THREE + 0x0014, // #DEVICE CONTROL FOUR + 0x0015, // #NEGATIVE ACKNOWLEDGE + 0x0016, // #SYNCHRONOUS IDLE + 0x0017, // #END OF TRANSMISSION BLOCK + 0x0018, // #CANCEL + 0x0019, // #END OF MEDIUM + 0x001a, // #SUBSTITUTE + 0x001b, // #ESCAPE + 0x001c, // #FILE SEPARATOR + 0x001d, // #GROUP SEPARATOR + 0x001e, // #RECORD SEPARATOR + 0x001f, // #UNIT SEPARATOR + 0x0020, // #SPACE + 0x0021, // #EXCLAMATION MARK + 0x0022, // #QUOTATION MARK + 0x0023, // #NUMBER SIGN + 0x0024, // #DOLLAR SIGN + 0x0025, // #PERCENT SIGN + 0x0026, // #AMPERSAND + 0x0027, // #APOSTROPHE + 0x0028, // #LEFT PARENTHESIS + 0x0029, // #RIGHT PARENTHESIS + 0x002a, // #ASTERISK + 0x002b, // #PLUS SIGN + 0x002c, // #COMMA + 0x002d, // #HYPHEN-MINUS + 0x002e, // #FULL STOP + 0x002f, // #SOLIDUS + 0x0030, // #DIGIT ZERO + 0x0031, // #DIGIT ONE + 0x0032, // #DIGIT TWO + 0x0033, // #DIGIT THREE + 0x0034, // #DIGIT FOUR + 0x0035, // #DIGIT FIVE + 0x0036, // #DIGIT SIX + 0x0037, // #DIGIT SEVEN + 0x0038, // #DIGIT EIGHT + 0x0039, // #DIGIT NINE + 0x003a, // #COLON + 0x003b, // #SEMICOLON + 0x003c, // #LESS-THAN SIGN + 0x003d, // #EQUALS SIGN + 0x003e, // #GREATER-THAN SIGN + 0x003f, // #QUESTION MARK + 0x0040, // #COMMERCIAL AT + 0x0041, // #LATIN CAPITAL LETTER A + 0x0042, // #LATIN CAPITAL LETTER B + 0x0043, // #LATIN CAPITAL LETTER C + 0x0044, // #LATIN CAPITAL LETTER D + 0x0045, // #LATIN CAPITAL LETTER E + 0x0046, // #LATIN CAPITAL LETTER F + 0x0047, // #LATIN CAPITAL LETTER G + 0x0048, // #LATIN CAPITAL LETTER H + 0x0049, // #LATIN CAPITAL LETTER I + 0x004a, // #LATIN CAPITAL LETTER J + 0x004b, // #LATIN CAPITAL LETTER K + 0x004c, // #LATIN CAPITAL LETTER L + 0x004d, // #LATIN CAPITAL LETTER M + 0x004e, // #LATIN CAPITAL LETTER N + 0x004f, // #LATIN CAPITAL LETTER O + 0x0050, // #LATIN CAPITAL LETTER P + 0x0051, // #LATIN CAPITAL LETTER Q + 0x0052, // #LATIN CAPITAL LETTER R + 0x0053, // #LATIN CAPITAL LETTER S + 0x0054, // #LATIN CAPITAL LETTER T + 0x0055, // #LATIN CAPITAL LETTER U + 0x0056, // #LATIN CAPITAL LETTER V + 0x0057, // #LATIN CAPITAL LETTER W + 0x0058, // #LATIN CAPITAL LETTER X + 0x0059, // #LATIN CAPITAL LETTER Y + 0x005a, // #LATIN CAPITAL LETTER Z + 0x005b, // #LEFT SQUARE BRACKET + 0x005c, // #REVERSE SOLIDUS + 0x005d, // #RIGHT SQUARE BRACKET + 0x005e, // #CIRCUMFLEX ACCENT + 0x005f, // #LOW LINE + 0x0060, // #GRAVE ACCENT + 0x0061, // #LATIN SMALL LETTER A + 0x0062, // #LATIN SMALL LETTER B + 0x0063, // #LATIN SMALL LETTER C + 0x0064, // #LATIN SMALL LETTER D + 0x0065, // #LATIN SMALL LETTER E + 0x0066, // #LATIN SMALL LETTER F + 0x0067, // #LATIN SMALL LETTER G + 0x0068, // #LATIN SMALL LETTER H + 0x0069, // #LATIN SMALL LETTER I + 0x006a, // #LATIN SMALL LETTER J + 0x006b, // #LATIN SMALL LETTER K + 0x006c, // #LATIN SMALL LETTER L + 0x006d, // #LATIN SMALL LETTER M + 0x006e, // #LATIN SMALL LETTER N + 0x006f, // #LATIN SMALL LETTER O + 0x0070, // #LATIN SMALL LETTER P + 0x0071, // #LATIN SMALL LETTER Q + 0x0072, // #LATIN SMALL LETTER R + 0x0073, // #LATIN SMALL LETTER S + 0x0074, // #LATIN SMALL LETTER T + 0x0075, // #LATIN SMALL LETTER U + 0x0076, // #LATIN SMALL LETTER V + 0x0077, // #LATIN SMALL LETTER W + 0x0078, // #LATIN SMALL LETTER X + 0x0079, // #LATIN SMALL LETTER Y + 0x007a, // #LATIN SMALL LETTER Z + 0x007b, // #LEFT CURLY BRACKET + 0x007c, // #VERTICAL LINE + 0x007d, // #RIGHT CURLY BRACKET + 0x007e, // #TILDE + 0x007f, // #DELETE + 0x00c7, // #LATIN CAPITAL LETTER C WITH CEDILLA + 0x00fc, // #LATIN SMALL LETTER U WITH DIAERESIS + 0x00e9, // #LATIN SMALL LETTER E WITH ACUTE + 0x00e2, // #LATIN SMALL LETTER A WITH CIRCUMFLEX + 0x00e4, // #LATIN SMALL LETTER A WITH DIAERESIS + 0x00e0, // #LATIN SMALL LETTER A WITH GRAVE + 0x00e5, // #LATIN SMALL LETTER A WITH RING ABOVE + 0x00e7, // #LATIN SMALL LETTER C WITH CEDILLA + 0x00ea, // #LATIN SMALL LETTER E WITH CIRCUMFLEX + 0x00eb, // #LATIN SMALL LETTER E WITH DIAERESIS + 0x00e8, // #LATIN SMALL LETTER E WITH GRAVE + 0x00ef, // #LATIN SMALL LETTER I WITH DIAERESIS + 0x00ee, // #LATIN SMALL LETTER I WITH CIRCUMFLEX + 0x00ec, // #LATIN SMALL LETTER I WITH GRAVE + 0x00c4, // #LATIN CAPITAL LETTER A WITH DIAERESIS + 0x00c5, // #LATIN CAPITAL LETTER A WITH RING ABOVE + 0x00c9, // #LATIN CAPITAL LETTER E WITH ACUTE + 0x00e6, // #LATIN SMALL LIGATURE AE + 0x00c6, // #LATIN CAPITAL LIGATURE AE + 0x00f4, // #LATIN SMALL LETTER O WITH CIRCUMFLEX + 0x00f6, // #LATIN SMALL LETTER O WITH DIAERESIS + 0x00f2, // #LATIN SMALL LETTER O WITH GRAVE + 0x00fb, // #LATIN SMALL LETTER U WITH CIRCUMFLEX + 0x00f9, // #LATIN SMALL LETTER U WITH GRAVE + 0x00ff, // #LATIN SMALL LETTER Y WITH DIAERESIS + 0x00d6, // #LATIN CAPITAL LETTER O WITH DIAERESIS + 0x00dc, // #LATIN CAPITAL LETTER U WITH DIAERESIS + 0x00a2, // #CENT SIGN + 0x00a3, // #POUND SIGN + 0x00a5, // #YEN SIGN + 0x20a7, // #PESETA SIGN + 0x0192, // #LATIN SMALL LETTER F WITH HOOK + 0x00e1, // #LATIN SMALL LETTER A WITH ACUTE + 0x00ed, // #LATIN SMALL LETTER I WITH ACUTE + 0x00f3, // #LATIN SMALL LETTER O WITH ACUTE + 0x00fa, // #LATIN SMALL LETTER U WITH ACUTE + 0x00f1, // #LATIN SMALL LETTER N WITH TILDE + 0x00d1, // #LATIN CAPITAL LETTER N WITH TILDE + 0x00aa, // #FEMININE ORDINAL INDICATOR + 0x00ba, // #MASCULINE ORDINAL INDICATOR + 0x00bf, // #INVERTED QUESTION MARK + 0x2310, // #REVERSED NOT SIGN + 0x00ac, // #NOT SIGN + 0x00bd, // #VULGAR FRACTION ONE HALF + 0x00bc, // #VULGAR FRACTION ONE QUARTER + 0x00a1, // #INVERTED EXCLAMATION MARK + 0x00ab, // #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x00bb, // #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x2591, // #LIGHT SHADE + 0x2592, // #MEDIUM SHADE + 0x2593, // #DARK SHADE + 0x2502, // #BOX DRAWINGS LIGHT VERTICAL + 0x2524, // #BOX DRAWINGS LIGHT VERTICAL AND LEFT + 0x2561, // #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + 0x2562, // #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + 0x2556, // #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + 0x2555, // #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + 0x2563, // #BOX DRAWINGS DOUBLE VERTICAL AND LEFT + 0x2551, // #BOX DRAWINGS DOUBLE VERTICAL + 0x2557, // #BOX DRAWINGS DOUBLE DOWN AND LEFT + 0x255d, // #BOX DRAWINGS DOUBLE UP AND LEFT + 0x255c, // #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + 0x255b, // #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + 0x2510, // #BOX DRAWINGS LIGHT DOWN AND LEFT + 0x2514, // #BOX DRAWINGS LIGHT UP AND RIGHT + 0x2534, // #BOX DRAWINGS LIGHT UP AND HORIZONTAL + 0x252c, // #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + 0x251c, // #BOX DRAWINGS LIGHT VERTICAL AND RIGHT + 0x2500, // #BOX DRAWINGS LIGHT HORIZONTAL + 0x253c, // #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + 0x255e, // #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + 0x255f, // #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + 0x255a, // #BOX DRAWINGS DOUBLE UP AND RIGHT + 0x2554, // #BOX DRAWINGS DOUBLE DOWN AND RIGHT + 0x2569, // #BOX DRAWINGS DOUBLE UP AND HORIZONTAL + 0x2566, // #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + 0x2560, // #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + 0x2550, // #BOX DRAWINGS DOUBLE HORIZONTAL + 0x256c, // #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + 0x2567, // #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + 0x2568, // #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + 0x2564, // #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + 0x2565, // #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + 0x2559, // #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + 0x2558, // #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + 0x2552, // #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + 0x2553, // #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + 0x256b, // #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + 0x256a, // #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + 0x2518, // #BOX DRAWINGS LIGHT UP AND LEFT + 0x250c, // #BOX DRAWINGS LIGHT DOWN AND RIGHT + 0x2588, // #FULL BLOCK + 0x2584, // #LOWER HALF BLOCK + 0x258c, // #LEFT HALF BLOCK + 0x2590, // #RIGHT HALF BLOCK + 0x2580, // #UPPER HALF BLOCK + 0x03b1, // #GREEK SMALL LETTER ALPHA + 0x00df, // #LATIN SMALL LETTER SHARP S + 0x0393, // #GREEK CAPITAL LETTER GAMMA + 0x03c0, // #GREEK SMALL LETTER PI + 0x03a3, // #GREEK CAPITAL LETTER SIGMA + 0x03c3, // #GREEK SMALL LETTER SIGMA + 0x00b5, // #MICRO SIGN + 0x03c4, // #GREEK SMALL LETTER TAU + 0x03a6, // #GREEK CAPITAL LETTER PHI + 0x0398, // #GREEK CAPITAL LETTER THETA + 0x03a9, // #GREEK CAPITAL LETTER OMEGA + 0x03b4, // #GREEK SMALL LETTER DELTA + 0x221e, // #INFINITY + 0x03c6, // #GREEK SMALL LETTER PHI + 0x03b5, // #GREEK SMALL LETTER EPSILON + 0x2229, // #INTERSECTION + 0x2261, // #IDENTICAL TO + 0x00b1, // #PLUS-MINUS SIGN + 0x2265, // #GREATER-THAN OR EQUAL TO + 0x2264, // #LESS-THAN OR EQUAL TO + 0x2320, // #TOP HALF INTEGRAL + 0x2321, // #BOTTOM HALF INTEGRAL + 0x00f7, // #DIVISION SIGN + 0x2248, // #ALMOST EQUAL TO + 0x00b0, // #DEGREE SIGN + 0x2219, // #BULLET OPERATOR + 0x00b7, // #MIDDLE DOT + 0x221a, // #SQUARE ROOT + 0x207f, // #SUPERSCRIPT LATIN SMALL LETTER N + 0x00b2, // #SUPERSCRIPT TWO + 0x25a0, // #BLACK SQUARE + 0x00a0, // #NO-BREAK SPACE + }; + + public char map_cp850_unicode(char x) { + if (x >= 0x100) + return x; + return unimap[x]; + } + + private void _SetCursor(int row, int col) { + int maxr = height - 1; + int tm = getTopMargin(); + + R = (row < 0)?0: row; + C = (col < 0)?0: (col >= width) ? width - 1 : col; + + if (!moveoutsidemargins) { + R += tm; + maxr = getBottomMargin(); + } + if (R > maxr) R = maxr; + } + + private void putChar(char c, boolean isWide, boolean doshowcursor) { + int rows = this.height; //statusline + int columns = this.width; + // byte msg[]; + +// if (debug > 4) { +// debugStr.append("putChar(") +// .append(c) +// .append(" [") +// .append((int) c) +// .append("]) at R=") +// .append(R) +// .append(" , C=") +// .append(C) +// .append(", columns=") +// .append(columns) +// .append(", rows=") +// .append(rows); +// debug(debugStr.toString()); +// debugStr.setLength(0); +// } +// markLine(R, 1); +// if (c > 255) { +// if (debug > 0) +// debug("char > 255:" + (int) c); +// //return; +// } + + switch (term_state) { + case TSTATE_DATA: + /* FIXME: we shouldn't use chars with bit 8 set if ibmcharset. + * probably... but some BBS do anyway... + */ + if (!useibmcharset) { + boolean doneflag = true; + switch (c) { + case OSC: + osc = ""; + term_state = TSTATE_OSC; + break; + case RI: + if (R > getTopMargin()) + R--; + else + insertLine(R, 1, SCROLL_DOWN); + if (debug > 1) + debug("RI"); + break; + case IND: + if (debug > 2) { + debugStr.append("IND at ") + .append(R) + .append(", tm is ") + .append(getTopMargin()) + .append(", bm is ") + .append(getBottomMargin()); + debug(debugStr.toString()); + debugStr.setLength(0); + } + if (R == getBottomMargin() || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + if (debug > 1) + debug("IND (at " + R + " )"); + break; + case NEL: + if (R == getBottomMargin() || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + C = 0; + if (debug > 1) + debug("NEL (at " + R + " )"); + break; + case HTS: + Tabs[C] = 1; + if (debug > 1) + debug("HTS"); + break; + case DCS: + dcs = ""; + term_state = TSTATE_DCS; + break; + default: + doneflag = false; + break; + } + if (doneflag) break; + } + switch (c) { + case SS3: + onegl = 3; + break; + case SS2: + onegl = 2; + break; + case CSI: // should be in the 8bit section, but some BBS use this + DCEvar = 0; + DCEvars[0] = 0; + DCEvars[1] = 0; + DCEvars[2] = 0; + DCEvars[3] = 0; + term_state = TSTATE_CSI; + break; + case ESC: + term_state = TSTATE_ESC; + lastwaslf = 0; + break; + case 5: /* ENQ */ + write(answerBack, false); + break; + case 12: + /* FormFeed, Home for the BBS world */ + deleteArea(0, 0, columns, rows, attributes); + C = R = 0; + break; + case '\b': /* 8 */ + C--; + if (C < 0) + C = 0; + lastwaslf = 0; + break; + case '\t': + do { + // Don't overwrite or insert! TABS are not destructive, but movement! + C++; + } while (C < columns && (Tabs[C] == 0)); + lastwaslf = 0; + break; + case '\r': // 13 CR + C = 0; + break; + case '\n': // 10 LF + if (debug > 3) + debug("R= " + R + ", bm " + getBottomMargin() + ", tm=" + getTopMargin() + ", rows=" + rows); + if (!vms) { + if (lastwaslf != 0 && lastwaslf != c) // Ray: I do not understand this logic. + break; + lastwaslf = c; + /*C = 0;*/ + } + if (R == getBottomMargin() || R >= rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + break; + case 7: + beep(); + break; + case '\016': /* SMACS , as */ + /* ^N, Shift out - Put G1 into GL */ + gl = 1; + usedcharsets = true; + break; + case '\017': /* RMACS , ae */ + /* ^O, Shift in - Put G0 into GL */ + gl = 0; + usedcharsets = true; + break; + default: + { + int thisgl = gl; + + if (onegl >= 0) { + thisgl = onegl; + onegl = -1; + } + lastwaslf = 0; + if (c < 32) { + if (c != 0) + if (debug > 0) + debug("TSTATE_DATA char: " + ((int) c)); + /*break; some BBS really want those characters, like hearst etc. */ + if (c == 0) /* print 0 ... you bet */ + break; + } + if (C >= columns) { + if (wraparound) { + int bot = rows; + + // If we're in the scroll region, check against the bottom margin + if (R <= getBottomMargin() && R >= getTopMargin()) + bot = getBottomMargin() + 1; + + if (R < bot - 1) + R++; + else { + if (debug > 3) debug("scrolling due to wrap at " + R); + insertLine(R, 1, SCROLL_UP); + } + C = 0; + } else { + // cursor stays on last character. + C = columns - 1; + } + } + + boolean mapped = false; + + // Mapping if DEC Special is chosen charset + if (usedcharsets) { + if (c >= '\u0020' && c <= '\u007f') { + switch (gx[thisgl]) { + case '0': + // Remap SCOANSI line drawing to VT100 line drawing chars + // for our SCO using customers. + if (terminalID.equals("scoansi") || terminalID.equals("ansi")) { + for (int i = 0; i < scoansi_acs.length(); i += 2) { + if (c == scoansi_acs.charAt(i)) { + c = scoansi_acs.charAt(i + 1); + break; + } + } + } + if (c >= '\u005f' && c <= '\u007e') { + c = DECSPECIAL[(short) c - 0x5f]; + mapped = true; + } + break; + case '<': // 'user preferred' is currently 'ISO Latin-1 suppl + c = (char) ((c & 0x7f) | 0x80); + mapped = true; + break; + case 'A': + case 'B': // Latin-1 , ASCII -> fall through + mapped = true; + break; + default: + debug("Unsupported GL mapping: " + gx[thisgl]); + break; + } + } + if (!mapped && (c >= '\u0080' && c <= '\u00ff')) { + switch (gx[gr]) { + case '0': + if (c >= '\u00df' && c <= '\u00fe') { + c = DECSPECIAL[c - '\u00df']; + mapped = true; + } + break; + case '<': + case 'A': + case 'B': + mapped = true; + break; + default: + debug("Unsupported GR mapping: " + gx[gr]); + break; + } + } + } + if (!mapped && useibmcharset) + c = map_cp850_unicode(c); + + /*if(true || (statusmode == 0)) { */ + if (isWide) { + if (C >= columns - 1) { + if (wraparound) { + int bot = rows; + + // If we're in the scroll region, check against the bottom margin + if (R <= getBottomMargin() && R >= getTopMargin()) + bot = getBottomMargin() + 1; + + if (R < bot - 1) + R++; + else { + if (debug > 3) debug("scrolling due to wrap at " + R); + insertLine(R, 1, SCROLL_UP); + } + C = 0; + } else { + // cursor stays on last wide character. + C = columns - 2; + } + } + } + + if (insertmode == 1) { + if (isWide) { + insertChar(C++, R, c, attributes | FULLWIDTH); + insertChar(C, R, ' ', attributes | FULLWIDTH); + } else + insertChar(C, R, c, attributes); + } else { + if (isWide) { + putChar(C++, R, c, attributes | FULLWIDTH); + putChar(C, R, ' ', attributes | FULLWIDTH); + } else + putChar(C, R, c, attributes); + } + + /* + } else { + if (insertmode==1) { + insertChar(C, rows, c, attributes); + } else { + putChar(C, rows, c, attributes); + } + } + */ + C++; + break; + } + } /* switch(c) */ + break; + case TSTATE_OSC: + if ((c < 0x20) && (c != ESC)) {// NP - No printing character + handle_osc(osc); + term_state = TSTATE_DATA; + break; + } + //but check for vt102 ESC \ + if (c == '\\' && osc.charAt(osc.length() - 1) == ESC) { + handle_osc(osc); + term_state = TSTATE_DATA; + break; + } + osc = osc + c; + break; + case TSTATE_ESCSPACE: + term_state = TSTATE_DATA; + switch (c) { + case 'F': /* S7C1T, Disable output of 8-bit controls, use 7-bit */ + output8bit = false; + break; + case 'G': /* S8C1T, Enable output of 8-bit control codes*/ + output8bit = true; + break; + default: + debug("ESC <space> " + c + " unhandled."); + } + break; + case TSTATE_ESC: + term_state = TSTATE_DATA; + switch (c) { + case ' ': + term_state = TSTATE_ESCSPACE; + break; + case '#': + term_state = TSTATE_ESCSQUARE; + break; + case 'c': + /* Hard terminal reset */ + reset(); + break; + case '[': + DCEvar = 0; + DCEvars[0] = 0; + DCEvars[1] = 0; + DCEvars[2] = 0; + DCEvars[3] = 0; + term_state = TSTATE_CSI; + break; + case ']': + osc = ""; + term_state = TSTATE_OSC; + break; + case 'P': + dcs = ""; + term_state = TSTATE_DCS; + break; + case 'A': /* CUU */ + R--; + if (R < 0) R = 0; + break; + case 'B': /* CUD */ + R++; + if (R >= rows) R = rows - 1; + break; + case 'C': + C++; + if (C >= columns) C = columns - 1; + break; + case 'I': // RI + insertLine(R, 1, SCROLL_DOWN); + break; + case 'E': /* NEL */ + if (R == getBottomMargin() || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + C = 0; + if (debug > 1) + debug("ESC E (at " + R + ")"); + break; + case 'D': /* IND */ + if (R == getBottomMargin() || R == rows - 1) + insertLine(R, 1, SCROLL_UP); + else + R++; + if (debug > 1) + debug("ESC D (at " + R + " )"); + break; + case 'J': /* erase to end of screen */ + if (R < rows - 1) + deleteArea(0, R + 1, columns, rows - R - 1, attributes); + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 'K': + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 'M': // RI + debug("ESC M : R is "+R+", tm is "+getTopMargin()+", bm is "+getBottomMargin()); + if (R > getTopMargin()) { // just go up 1 line. + R--; + } else { // scroll down + insertLine(R, 1, SCROLL_DOWN); + } + /* else do nothing ; */ + if (debug > 2) + debug("ESC M "); + break; + case 'H': + if (debug > 1) + debug("ESC H at " + C); + /* right border probably ...*/ + if (C >= columns) + C = columns - 1; + Tabs[C] = 1; + break; + case 'N': // SS2 + onegl = 2; + break; + case 'O': // SS3 + onegl = 3; + break; + case '=': + /*application keypad*/ + if (debug > 0) + debug("ESC ="); + keypadmode = true; + break; + case '<': /* vt52 mode off */ + vt52mode = false; + break; + case '>': /*normal keypad*/ + if (debug > 0) + debug("ESC >"); + keypadmode = false; + break; + case '7': /* DECSC: save cursor, attributes */ + Sc = C; + Sr = R; + Sgl = gl; + Sgr = gr; + Sa = attributes; + Sgx = new char[4]; + for (int i = 0; i < 4; i++) Sgx[i] = gx[i]; + if (debug > 1) + debug("ESC 7"); + break; + case '8': /* DECRC: restore cursor, attributes */ + C = Sc; + R = Sr; + gl = Sgl; + gr = Sgr; + if (Sgx != null) + for (int i = 0; i < 4; i++) gx[i] = Sgx[i]; + attributes = Sa; + if (debug > 1) + debug("ESC 8"); + break; + case '(': /* Designate G0 Character set (ISO 2022) */ + term_state = TSTATE_SETG0; + usedcharsets = true; + break; + case ')': /* Designate G1 character set (ISO 2022) */ + term_state = TSTATE_SETG1; + usedcharsets = true; + break; + case '*': /* Designate G2 Character set (ISO 2022) */ + term_state = TSTATE_SETG2; + usedcharsets = true; + break; + case '+': /* Designate G3 Character set (ISO 2022) */ + term_state = TSTATE_SETG3; + usedcharsets = true; + break; + case '~': /* Locking Shift 1, right */ + gr = 1; + usedcharsets = true; + break; + case 'n': /* Locking Shift 2 */ + gl = 2; + usedcharsets = true; + break; + case '}': /* Locking Shift 2, right */ + gr = 2; + usedcharsets = true; + break; + case 'o': /* Locking Shift 3 */ + gl = 3; + usedcharsets = true; + break; + case '|': /* Locking Shift 3, right */ + gr = 3; + usedcharsets = true; + break; + case 'Y': /* vt52 cursor address mode , next chars are x,y */ + term_state = TSTATE_VT52Y; + break; + case '_': + term_state = TSTATE_TITLE; + break; + case '\\': + // TODO save title + term_state = TSTATE_DATA; + break; + default: + debug("ESC unknown letter: " + c + " (" + ((int) c) + ")"); + break; + } + break; + case TSTATE_VT52X: + C = c - 37; + if (C < 0) + C = 0; + else if (C >= width) + C = width - 1; + term_state = TSTATE_VT52Y; + break; + case TSTATE_VT52Y: + R = c - 37; + if (R < 0) + R = 0; + else if (R >= height) + R = height - 1; + term_state = TSTATE_DATA; + break; + case TSTATE_SETG0: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + debug("ESC ( " + c + ": G0 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) debug("ESC ( : G0 char set (" + c + " " + ((int) c) + ")"); + gx[0] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG1: + if (c != '0' && c != 'A' && c != 'B' && c != '<') { + debug("ESC ) " + c + " (" + ((int) c) + ") :G1 char set?"); + } else { + if (debug > 2) debug("ESC ) :G1 char set (" + c + " " + ((int) c) + ")"); + gx[1] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG2: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + debug("ESC*:G2 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) debug("ESC*:G2 char set (" + c + " " + ((int) c) + ")"); + gx[2] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_SETG3: + if (c != '0' && c != 'A' && c != 'B' && c != '<') + debug("ESC+:G3 char set? (" + ((int) c) + ")"); + else { + if (debug > 2) debug("ESC+:G3 char set (" + c + " " + ((int) c) + ")"); + gx[3] = c; + } + term_state = TSTATE_DATA; + break; + case TSTATE_ESCSQUARE: + switch (c) { + case '8': + for (int i = 0; i < columns; i++) + for (int j = 0; j < rows; j++) + putChar(i, j, 'E', 0); + break; + default: + debug("ESC # " + c + " not supported."); + break; + } + term_state = TSTATE_DATA; + break; + case TSTATE_DCS: + if (c == '\\' && dcs.charAt(dcs.length() - 1) == ESC) { + handle_dcs(dcs); + term_state = TSTATE_DATA; + break; + } + dcs = dcs + c; + break; + + case TSTATE_DCEQ: + term_state = TSTATE_DATA; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48; + term_state = TSTATE_DCEQ; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_DCEQ; + break; + case 's': // XTERM_SAVE missing! + if (true || debug > 1) + debug("ESC [ ? " + DCEvars[0] + " s unimplemented!"); + break; + case 'r': // XTERM_RESTORE + if (true || debug > 1) + debug("ESC [ ? " + DCEvars[0] + " r"); + /* DEC Mode reset */ + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 3: /* 80 columns*/ + setScreenSize(80, height, true); + break; + case 4: /* scrolling mode, smooth */ + break; + case 5: /* light background */ + break; + case 6: /* DECOM (Origin Mode) move inside margins. */ + moveoutsidemargins = true; + break; + case 7: /* DECAWM: Autowrap Mode */ + wraparound = false; + break; + case 12:/* local echo off */ + break; + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report on */ + case 1001: + case 1002: + case 1003: + mouserpt = DCEvars[i]; + break; + default: + debug("ESC [ ? " + DCEvars[0] + " r, unimplemented!"); + } + } + break; + case 'h': // DECSET + if (debug > 0) + debug("ESC [ ? " + DCEvars[0] + " h"); + /* DEC Mode set */ + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 1: /* Application cursor keys */ + KeyUp[0] = "\u001bOA"; + KeyDown[0] = "\u001bOB"; + KeyRight[0] = "\u001bOC"; + KeyLeft[0] = "\u001bOD"; + break; + case 2: /* DECANM */ + vt52mode = false; + break; + case 3: /* 132 columns*/ + setScreenSize(132, height, true); + break; + case 6: /* DECOM: move inside margins. */ + moveoutsidemargins = false; + break; + case 7: /* DECAWM: Autowrap Mode */ + wraparound = true; + break; + case 25: /* turn cursor on */ + showCursor(true); + break; + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report on */ + case 1001: + case 1002: + case 1003: + mouserpt = DCEvars[i]; + break; + + /* unimplemented stuff, fall through */ + /* 4 - scrolling mode, smooth */ + /* 5 - light background */ + /* 12 - local echo off */ + /* 18 - DECPFF - Printer Form Feed Mode -> On */ + /* 19 - DECPEX - Printer Extent Mode -> Screen */ + default: + debug("ESC [ ? " + DCEvars[0] + " h, unsupported."); + break; + } + } + break; + case 'i': // DEC Printer Control, autoprint, echo screenchars to printer + // This is different to CSI i! + // Also: "Autoprint prints a final display line only when the + // cursor is moved off the line by an autowrap or LF, FF, or + // VT (otherwise do not print the line)." + switch (DCEvars[0]) { + case 1: + if (debug > 1) + debug("CSI ? 1 i : Print line containing cursor"); + break; + case 4: + if (debug > 1) + debug("CSI ? 4 i : Start passthrough printing"); + break; + case 5: + if (debug > 1) + debug("CSI ? 4 i : Stop passthrough printing"); + break; + } + break; + case 'l': //DECRST + /* DEC Mode reset */ + if (debug > 0) + debug("ESC [ ? " + DCEvars[0] + " l"); + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 1: /* Application cursor keys */ + KeyUp[0] = "\u001b[A"; + KeyDown[0] = "\u001b[B"; + KeyRight[0] = "\u001b[C"; + KeyLeft[0] = "\u001b[D"; + break; + case 2: /* DECANM */ + vt52mode = true; + break; + case 3: /* 80 columns*/ + setScreenSize(80, height, true); + break; + case 6: /* DECOM: move outside margins. */ + moveoutsidemargins = true; + break; + case 7: /* DECAWM: Autowrap Mode OFF */ + wraparound = false; + break; + case 25: /* turn cursor off */ + showCursor(false); + break; + /* Unimplemented stuff: */ + /* 4 - scrolling mode, jump */ + /* 5 - dark background */ + /* 7 - DECAWM - no wrap around mode */ + /* 12 - local echo on */ + /* 18 - DECPFF - Printer Form Feed Mode -> Off*/ + /* 19 - DECPEX - Printer Extent Mode -> Scrolling Region */ + case 9: /* X10 mouse */ + case 1000: /* xterm style mouse report OFF */ + case 1001: + case 1002: + case 1003: + mouserpt = 0; + break; + default: + debug("ESC [ ? " + DCEvars[0] + " l, unsupported."); + break; + } + } + break; + case 'n': + if (debug > 0) + debug("ESC [ ? " + DCEvars[0] + " n"); + switch (DCEvars[0]) { + case 15: + /* printer? no printer. */ + write((ESC) + "[?13n", false); + debug("ESC[5n"); + break; + default: + debug("ESC [ ? " + DCEvars[0] + " n, unsupported."); + break; + } + break; + default: + debug("ESC [ ? " + DCEvars[0] + " " + c + ", unsupported."); + break; + } + break; + case TSTATE_CSI_EX: + term_state = TSTATE_DATA; + switch (c) { + case ESC: + term_state = TSTATE_ESC; + break; + default: + debug("Unknown character ESC[! character is " + (int) c); + break; + } + break; + case TSTATE_CSI_TICKS: + term_state = TSTATE_DATA; + switch (c) { + case 'p': + debug("Conformance level: " + DCEvars[0] + " (unsupported)," + DCEvars[1]); + if (DCEvars[0] == 61) { + output8bit = false; + break; + } + if (DCEvars[1] == 1) { + output8bit = false; + } else { + output8bit = true; /* 0 or 2 */ + } + break; + default: + debug("Unknown ESC [... \"" + c); + break; + } + break; + case TSTATE_CSI_EQUAL: + term_state = TSTATE_DATA; + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48; + term_state = TSTATE_CSI_EQUAL; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_CSI_EQUAL; + break; + + case 'F': /* SCO ANSI foreground */ + { + int newcolor; + + debug("ESC [ = "+DCEvars[0]+" F"); + + attributes &= ~COLOR_FG; + newcolor = ((DCEvars[0] & 1) << 2) | + (DCEvars[0] & 2) | + ((DCEvars[0] & 4) >> 2) ; + attributes |= (newcolor+1) << COLOR_FG_SHIFT; + + break; + } + case 'G': /* SCO ANSI background */ + { + int newcolor; + + debug("ESC [ = "+DCEvars[0]+" G"); + + attributes &= ~COLOR_BG; + newcolor = ((DCEvars[0] & 1) << 2) | + (DCEvars[0] & 2) | + ((DCEvars[0] & 4) >> 2) ; + attributes |= (newcolor+1) << COLOR_BG_SHIFT; + break; + } + + default: + debugStr.append("Unknown ESC [ = "); + for (int i=0;i<=DCEvar;i++) { + debugStr.append(DCEvars[i]) + .append(','); + } + debugStr.append(c); + debug(debugStr.toString()); + debugStr.setLength(0); + break; + } + break; + case TSTATE_CSI_DOLLAR: + term_state = TSTATE_DATA; + switch (c) { + case '}': + debug("Active Status Display now " + DCEvars[0]); + statusmode = DCEvars[0]; + break; + /* bad documentation? + case '-': + debug("Set Status Display now "+DCEvars[0]); + break; + */ + case '~': + debug("Status Line mode now " + DCEvars[0]); + break; + default: + debug("UNKNOWN Status Display code " + c + ", with Pn=" + DCEvars[0]); + break; + } + break; + case TSTATE_CSI: + term_state = TSTATE_DATA; + switch (c) { + case '"': + term_state = TSTATE_CSI_TICKS; + break; + case '$': + term_state = TSTATE_CSI_DOLLAR; + break; + case '=': + term_state = TSTATE_CSI_EQUAL; + break; + case '!': + term_state = TSTATE_CSI_EX; + break; + case '?': + DCEvar = 0; + DCEvars[0] = 0; + term_state = TSTATE_DCEQ; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + DCEvars[DCEvar] = DCEvars[DCEvar] * 10 + (c) - 48; + term_state = TSTATE_CSI; + break; + case ';': + DCEvar++; + DCEvars[DCEvar] = 0; + term_state = TSTATE_CSI; + break; + case 'c':/* send primary device attributes */ + /* send (ESC[?61c) */ + + String subcode = ""; + if (terminalID.equals("vt320")) subcode = "63;"; + if (terminalID.equals("vt220")) subcode = "62;"; + if (terminalID.equals("vt100")) subcode = "61;"; + write((ESC) + "[?" + subcode + "1;2c", false); + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " c"); + break; + case 'q': + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " q"); + break; + case 'g': + /* used for tabsets */ + switch (DCEvars[0]) { + case 3:/* clear them */ + Tabs = new byte[width]; + break; + case 0: + Tabs[C] = 0; + break; + } + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " g"); + break; + case 'h': + switch (DCEvars[0]) { + case 4: + insertmode = 1; + break; + case 20: + debug("Setting CRLF to TRUE"); + sendcrlf = true; + break; + default: + debug("unsupported: ESC [ " + DCEvars[0] + " h"); + break; + } + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " h"); + break; + case 'i': // Printer Controller mode. + // "Transparent printing sends all output, except the CSI 4 i + // termination string, to the printer and not the screen, + // uses an 8-bit channel if no parity so NUL and DEL will be + // seen by the printer and by the termination recognizer code, + // and all translation and character set selections are + // bypassed." + switch (DCEvars[0]) { + case 0: + if (debug > 1) + debug("CSI 0 i: Print Screen, not implemented."); + break; + case 4: + if (debug > 1) + debug("CSI 4 i: Enable Transparent Printing, not implemented."); + break; + case 5: + if (debug > 1) + debug("CSI 4/5 i: Disable Transparent Printing, not implemented."); + break; + default: + debug("ESC [ " + DCEvars[0] + " i, unimplemented!"); + } + break; + case 'l': + switch (DCEvars[0]) { + case 4: + insertmode = 0; + break; + case 20: + debug("Setting CRLF to FALSE"); + sendcrlf = false; + break; + default: + debug("ESC [ " + DCEvars[0] + " l, unimplemented!"); + break; + } + break; + case 'A': // CUU + { + int limit; + /* FIXME: xterm only cares about 0 and topmargin */ + if (R >= getTopMargin()) { + limit = getTopMargin(); + } else + limit = 0; + if (DCEvars[0] == 0) + R--; + else + R -= DCEvars[0]; + if (R < limit) + R = limit; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " A"); + break; + } + case 'B': // CUD + /* cursor down n (1) times */ + { + int limit; + if (R <= getBottomMargin()) { + limit = getBottomMargin(); + } else + limit = rows - 1; + if (DCEvars[0] == 0) + R++; + else + R += DCEvars[0]; + if (R > limit) + R = limit; + else { + if (debug > 2) debug("Not limited."); + } + if (debug > 2) debug("to: " + R); + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " B (at C=" + C + ")"); + break; + } + case 'C': + if (DCEvars[0] == 0) + DCEvars[0] = 1; + while (DCEvars[0]-- > 0) { + C++; + } + if (C >= columns) + C = columns - 1; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " C"); + break; + case 'd': // CVA + R = DCEvars[0] - 1; + if (R < 0) + R = 0; + else if (R >= height) + R = height - 1; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " d"); + break; + case 'D': + if (DCEvars[0] == 0) + DCEvars[0] = 1; + while (DCEvars[0]-- > 0) { + C--; + } + if (C < 0) C = 0; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " D"); + break; + case 'r': // DECSTBM + if (DCEvar > 0) // Ray: Any argument is optional + { + R = DCEvars[1] - 1; + if (R < 0) + R = rows - 1; + else if (R >= rows) { + R = rows - 1; + } + } else + R = rows - 1; + int bot = R; + if (R >= DCEvars[0]) { + R = DCEvars[0] - 1; + if (R < 0) + R = 0; + } + setMargins(R, bot); + _SetCursor(0, 0); + if (debug > 1) + debug("ESC [" + DCEvars[0] + " ; " + DCEvars[1] + " r"); + break; + case 'G': /* CUP / cursor absolute column */ + C = DCEvars[0]; + if (C < 0) + C = 0; + else if (C >= width) + C = width - 1; + if (debug > 1) debug("ESC [ " + DCEvars[0] + " G"); + break; + case 'H': /* CUP / cursor position */ + /* gets 2 arguments */ + _SetCursor(DCEvars[0] - 1, DCEvars[1] - 1); + if (debug > 2) { + debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " H, moveoutsidemargins " + moveoutsidemargins); + debug(" -> R now " + R + ", C now " + C); + } + break; + case 'f': /* move cursor 2 */ + /* gets 2 arguments */ + R = DCEvars[0] - 1; + C = DCEvars[1] - 1; + if (C < 0) + C = 0; + else if (C >= width) + C = width - 1; + if (R < 0) + R = 0; + else if (R >= height) + R = height - 1; + if (debug > 2) + debug("ESC [ " + DCEvars[0] + ";" + DCEvars[1] + " f"); + break; + case 'S': /* ind aka 'scroll forward' */ + if (DCEvars[0] == 0) + insertLine(getBottomMargin(), SCROLL_UP); + else + insertLine(getBottomMargin(), DCEvars[0], SCROLL_UP); + break; + case 'L': + /* insert n lines */ + if (DCEvars[0] == 0) + insertLine(R, SCROLL_DOWN); + else + insertLine(R, DCEvars[0], SCROLL_DOWN); + if (debug > 1) + debug("ESC [ " + DCEvars[0] + "" + (c) + " (at R " + R + ")"); + break; + case 'T': /* 'ri' aka scroll backward */ + if (DCEvars[0] == 0) + insertLine(getTopMargin(), SCROLL_DOWN); + else + insertLine(getTopMargin(), DCEvars[0], SCROLL_DOWN); + break; + case 'M': + if (debug > 1) + debug("ESC [ " + DCEvars[0] + "" + (c) + " at R=" + R); + if (DCEvars[0] == 0) + deleteLine(R); + else + for (int i = 0; i < DCEvars[0]; i++) + deleteLine(R); + break; + case 'K': + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " K"); + /* clear in line */ + switch (DCEvars[0]) { + case 6: /* 97801 uses ESC[6K for delete to end of line */ + case 0:/*clear to right*/ + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 1:/*clear to the left, including this */ + if (C > 0) + deleteArea(0, R, C + 1, 1, attributes); + break; + case 2:/*clear whole line */ + deleteArea(0, R, columns, 1, attributes); + break; + } + break; + case 'J': + /* clear below current line */ + switch (DCEvars[0]) { + case 0: + if (R < rows - 1) + deleteArea(0, R + 1, columns, rows - R - 1, attributes); + if (C < columns - 1) + deleteArea(C, R, columns - C, 1, attributes); + break; + case 1: + if (R > 0) + deleteArea(0, 0, columns, R, attributes); + if (C > 0) + deleteArea(0, R, C + 1, 1, attributes);// include up to and including current + break; + case 2: + deleteArea(0, 0, columns, rows, attributes); + break; + } + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " J"); + break; + case '@': + if (DCEvars[0] == 0) DCEvars[0] = 1; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " @"); + for (int i = 0; i < DCEvars[0]; i++) + insertChar(C, R, ' ', attributes); + break; + case 'X': + { + int toerase = DCEvars[0]; + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " X, C=" + C + ",R=" + R); + if (toerase == 0) + toerase = 1; + if (toerase + C > columns) + toerase = columns - C; + deleteArea(C, R, toerase, 1, attributes); + // does not change cursor position + break; + } + case 'P': + if (debug > 1) + debug("ESC [ " + DCEvars[0] + " P, C=" + C + ",R=" + R); + if (DCEvars[0] == 0) DCEvars[0] = 1; + for (int i = 0; i < DCEvars[0]; i++) + deleteChar(C, R); + break; + case 'n': + switch (DCEvars[0]) { + case 5: /* malfunction? No malfunction. */ + writeSpecial((ESC) + "[0n"); + if (debug > 1) + debug("ESC[5n"); + break; + case 6: + // DO NOT offset R and C by 1! (checked against /usr/X11R6/bin/resize + // FIXME check again. + // FIXME: but vttest thinks different??? + writeSpecial((ESC) + "[" + R + ";" + C + "R"); + if (debug > 1) + debug("ESC[6n"); + break; + default: + if (debug > 0) + debug("ESC [ " + DCEvars[0] + " n??"); + break; + } + break; + case 's': /* DECSC - save cursor */ + Sc = C; + Sr = R; + Sa = attributes; + if (debug > 3) + debug("ESC[s"); + break; + case 'u': /* DECRC - restore cursor */ + C = Sc; + R = Sr; + attributes = Sa; + if (debug > 3) + debug("ESC[u"); + break; + case 'm': /* attributes as color, bold , blink,*/ + if (debug > 3) + debug("ESC [ "); + if (DCEvar == 0 && DCEvars[0] == 0) + attributes = 0; + for (int i = 0; i <= DCEvar; i++) { + switch (DCEvars[i]) { + case 0: + if (DCEvar > 0) { + if (terminalID.equals("scoansi")) { + attributes &= COLOR; /* Keeps color. Strange but true. */ + } else { + attributes = 0; + } + } + break; + case 1: + attributes |= BOLD; + attributes &= ~LOW; + break; + case 2: + /* SCO color hack mode */ + if (terminalID.equals("scoansi") && ((DCEvar - i) >= 2)) { + int ncolor; + attributes &= ~(COLOR | BOLD); + + ncolor = DCEvars[i + 1]; + if ((ncolor & 8) == 8) + attributes |= BOLD; + ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2); + attributes |= ((ncolor) + 1) << COLOR_FG_SHIFT; + ncolor = DCEvars[i + 2]; + ncolor = ((ncolor & 1) << 2) | (ncolor & 2) | ((ncolor & 4) >> 2); + attributes |= ((ncolor) + 1) << COLOR_BG_SHIFT; + i += 2; + } else { + attributes |= LOW; + } + break; + case 3: /* italics */ + attributes |= INVERT; + break; + case 4: + attributes |= UNDERLINE; + break; + case 7: + attributes |= INVERT; + break; + case 8: + attributes |= INVISIBLE; + break; + case 5: /* blink on */ + break; + /* 10 - ANSI X3.64-1979, select primary font, don't display control + * chars, don't set bit 8 on output */ + case 10: + gl = 0; + usedcharsets = true; + break; + /* 11 - ANSI X3.64-1979, select second alt. font, display control + * chars, set bit 8 on output */ + case 11: /* SMACS , as */ + case 12: + gl = 1; + usedcharsets = true; + break; + case 21: /* normal intensity */ + attributes &= ~(LOW | BOLD); + break; + case 23: /* italics off */ + attributes &= ~INVERT; + break; + case 25: /* blinking off */ + break; + case 27: + attributes &= ~INVERT; + break; + case 28: + attributes &= ~INVISIBLE; + break; + case 24: + attributes &= ~UNDERLINE; + break; + case 22: + attributes &= ~BOLD; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + attributes &= ~COLOR_FG; + attributes |= ((DCEvars[i] - 30) + 1)<< COLOR_FG_SHIFT; + break; + case 38: + if (DCEvars[i+1] == 5) { + attributes &= ~COLOR_FG; + attributes |= ((DCEvars[i + 2]) + 1) << COLOR_FG_SHIFT; + i += 2; + } + break; + case 39: + attributes &= ~COLOR_FG; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + attributes &= ~COLOR_BG; + attributes |= ((DCEvars[i] - 40) + 1) << COLOR_BG_SHIFT; + break; + case 48: + if (DCEvars[i+1] == 5) { + attributes &= ~COLOR_BG; + attributes |= (DCEvars[i + 2] + 1) << COLOR_BG_SHIFT; + i += 2; + } + break; + case 49: + attributes &= ~COLOR_BG; + break; + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + attributes &= ~COLOR_FG; + attributes |= ((DCEvars[i] - 82) + 1) << COLOR_FG_SHIFT; + break; + case 100: + case 101: + case 102: + case 103: + case 104: + case 105: + case 106: + case 107: + attributes &= ~COLOR_BG; + attributes |= ((DCEvars[i] - 92) + 1) << COLOR_BG_SHIFT; + break; + + default: + debugStr.append("ESC [ ") + .append(DCEvars[i]) + .append(" m unknown..."); + debug(debugStr.toString()); + debugStr.setLength(0); + break; + } + if (debug > 3) { + debugStr.append(DCEvars[i]) + .append(';'); + debug(debugStr.toString()); + debugStr.setLength(0); + } + } + if (debug > 3) { + debugStr.append(" (attributes = ") + .append(attributes) + .append(")m"); + debug(debugStr.toString()); + debugStr.setLength(0); + } + break; + default: + debugStr.append("ESC [ unknown letter: ") + .append(c) + .append(" (") + .append((int)c) + .append(')'); + debug(debugStr.toString()); + debugStr.setLength(0); + break; + } + break; + case TSTATE_TITLE: + switch (c) { + case ESC: + term_state = TSTATE_ESC; + break; + default: + // TODO save title + break; + } + break; + default: + term_state = TSTATE_DATA; + break; + } + + setCursorPosition(C, R); + } + + /* hard reset the terminal */ + public void reset() { + gx[0] = 'B'; + gx[1] = 'B'; + gx[2] = 'B'; + gx[3] = 'B'; + + gl = 0; // default GL to G0 + gr = 2; // default GR to G2 + + onegl = -1; // Single shift override + + /* reset tabs */ + int nw = width; + if (nw < 132) nw = 132; + Tabs = new byte[nw]; + for (int i = 0; i < nw; i += 8) { + Tabs[i] = 1; + } + + deleteArea(0, 0, width, height, attributes); + setMargins(0, height); + C = R = 0; + _SetCursor(0, 0); + + if (display != null) + display.resetColors(); + + showCursor(true); + /*FIXME:*/ + term_state = TSTATE_DATA; + } +} |