PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/emulatorview/src/jackpal/androidterm/emulatorview/TerminalEmulator.java

https://bitbucket.org/zielmicha/emacs-android-app
Java | 1572 lines | 1050 code | 228 blank | 294 comment | 209 complexity | a9173e7e1a66ee2a714a253073c9272c MD5 | raw file
  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package jackpal.androidterm.emulatorview;
  17. import java.nio.ByteBuffer;
  18. import java.nio.CharBuffer;
  19. import java.nio.charset.Charset;
  20. import java.nio.charset.CharsetDecoder;
  21. import java.nio.charset.CodingErrorAction;
  22. import android.util.Log;
  23. /**
  24. * Renders text into a screen. Contains all the terminal-specific knowlege and
  25. * state. Emulates a subset of the X Window System xterm terminal, which in turn
  26. * is an emulator for a subset of the Digital Equipment Corporation vt100
  27. * terminal. Missing functionality: text attributes (bold, underline, reverse
  28. * video, color) alternate screen cursor key and keypad escape sequences.
  29. */
  30. class TerminalEmulator {
  31. /**
  32. * The cursor row. Numbered 0..mRows-1.
  33. */
  34. private int mCursorRow;
  35. /**
  36. * The cursor column. Numbered 0..mColumns-1.
  37. */
  38. private int mCursorCol;
  39. /**
  40. * The number of character rows in the terminal screen.
  41. */
  42. private int mRows;
  43. /**
  44. * The number of character columns in the terminal screen.
  45. */
  46. private int mColumns;
  47. /**
  48. * Stores the characters that appear on the screen of the emulated terminal.
  49. */
  50. private Screen mScreen;
  51. /**
  52. * The terminal session this emulator is bound to.
  53. */
  54. private TermSession mSession;
  55. /**
  56. * Keeps track of the current argument of the current escape sequence.
  57. * Ranges from 0 to MAX_ESCAPE_PARAMETERS-1. (Typically just 0 or 1.)
  58. */
  59. private int mArgIndex;
  60. /**
  61. * The number of parameter arguments. This name comes from the ANSI standard
  62. * for terminal escape codes.
  63. */
  64. private static final int MAX_ESCAPE_PARAMETERS = 16;
  65. /**
  66. * Holds the arguments of the current escape sequence.
  67. */
  68. private int[] mArgs = new int[MAX_ESCAPE_PARAMETERS];
  69. // Escape processing states:
  70. /**
  71. * Escape processing state: Not currently in an escape sequence.
  72. */
  73. private static final int ESC_NONE = 0;
  74. /**
  75. * Escape processing state: Have seen an ESC character
  76. */
  77. private static final int ESC = 1;
  78. /**
  79. * Escape processing state: Have seen ESC POUND
  80. */
  81. private static final int ESC_POUND = 2;
  82. /**
  83. * Escape processing state: Have seen ESC and a character-set-select char
  84. */
  85. private static final int ESC_SELECT_LEFT_PAREN = 3;
  86. /**
  87. * Escape processing state: Have seen ESC and a character-set-select char
  88. */
  89. private static final int ESC_SELECT_RIGHT_PAREN = 4;
  90. /**
  91. * Escape processing state: ESC [
  92. */
  93. private static final int ESC_LEFT_SQUARE_BRACKET = 5;
  94. /**
  95. * Escape processing state: ESC [ ?
  96. */
  97. private static final int ESC_LEFT_SQUARE_BRACKET_QUESTION_MARK = 6;
  98. /**
  99. * Escape processing state: ESC %
  100. */
  101. private static final int ESC_PERCENT = 7;
  102. /**
  103. * True if the current escape sequence should continue, false if the current
  104. * escape sequence should be terminated. Used when parsing a single
  105. * character.
  106. */
  107. private boolean mContinueSequence;
  108. /**
  109. * The current state of the escape sequence state machine.
  110. */
  111. private int mEscapeState;
  112. /**
  113. * Saved state of the cursor row, Used to implement the save/restore cursor
  114. * position escape sequences.
  115. */
  116. private int mSavedCursorRow;
  117. /**
  118. * Saved state of the cursor column, Used to implement the save/restore
  119. * cursor position escape sequences.
  120. */
  121. private int mSavedCursorCol;
  122. // DecSet booleans
  123. /**
  124. * This mask indicates 132-column mode is set. (As opposed to 80-column
  125. * mode.)
  126. */
  127. private static final int K_132_COLUMN_MODE_MASK = 1 << 3;
  128. /**
  129. * This mask indicates that origin mode is set. (Cursor addressing is
  130. * relative to the absolute screen size, rather than the currently set top
  131. * and bottom margins.)
  132. */
  133. private static final int K_ORIGIN_MODE_MASK = 1 << 6;
  134. /**
  135. * This mask indicates that wraparound mode is set. (As opposed to
  136. * stop-at-right-column mode.)
  137. */
  138. private static final int K_WRAPAROUND_MODE_MASK = 1 << 7;
  139. /**
  140. * Holds multiple DECSET flags. The data is stored this way, rather than in
  141. * separate booleans, to make it easier to implement the save-and-restore
  142. * semantics. The various k*ModeMask masks can be used to extract and modify
  143. * the individual flags current states.
  144. */
  145. private int mDecFlags;
  146. /**
  147. * Saves away a snapshot of the DECSET flags. Used to implement save and
  148. * restore escape sequences.
  149. */
  150. private int mSavedDecFlags;
  151. // Modes set with Set Mode / Reset Mode
  152. /**
  153. * True if insert mode (as opposed to replace mode) is active. In insert
  154. * mode new characters are inserted, pushing existing text to the right.
  155. */
  156. private boolean mInsertMode;
  157. /**
  158. * Automatic newline mode. Configures whether pressing return on the
  159. * keyboard automatically generates a return as well. Not currently
  160. * implemented.
  161. */
  162. private boolean mAutomaticNewlineMode;
  163. /**
  164. * An array of tab stops. mTabStop[i] is true if there is a tab stop set for
  165. * column i.
  166. */
  167. private boolean[] mTabStop;
  168. // The margins allow portions of the screen to be locked.
  169. /**
  170. * The top margin of the screen, for scrolling purposes. Ranges from 0 to
  171. * mRows-2.
  172. */
  173. private int mTopMargin;
  174. /**
  175. * The bottom margin of the screen, for scrolling purposes. Ranges from
  176. * mTopMargin + 2 to mRows. (Defines the first row after the scrolling
  177. * region.
  178. */
  179. private int mBottomMargin;
  180. /**
  181. * True if the next character to be emitted will be automatically wrapped to
  182. * the next line. Used to disambiguate the case where the cursor is
  183. * positioned on column mColumns-1.
  184. */
  185. private boolean mAboutToAutoWrap;
  186. /**
  187. * The width of the last emitted spacing character. Used to place
  188. * combining characters into the correct column.
  189. */
  190. private int mLastEmittedCharWidth = 0;
  191. /**
  192. * True if we just auto-wrapped and no character has been emitted on this
  193. * line yet. Used to ensure combining characters following a character
  194. * at the edge of the screen are stored in the proper place.
  195. */
  196. private boolean mJustWrapped = false;
  197. /**
  198. * Used for debugging, counts how many chars have been processed.
  199. */
  200. private int mProcessedCharCount;
  201. /**
  202. * Foreground color, 0..7, mask with 8 for bold
  203. */
  204. private int mForeColor;
  205. private int mDefaultForeColor;
  206. /**
  207. * Background color, 0..7, mask with 8 for underline
  208. */
  209. private int mBackColor;
  210. private int mDefaultBackColor;
  211. private boolean mInverseColors;
  212. private boolean mbKeypadApplicationMode;
  213. private boolean mAlternateCharSet;
  214. /**
  215. * Special graphics character set
  216. */
  217. private static final char[] mSpecialGraphicsCharMap = new char[128];
  218. static {
  219. for (char i = 0; i < 128; ++i) {
  220. mSpecialGraphicsCharMap[i] = i;
  221. }
  222. mSpecialGraphicsCharMap['_'] = ' '; // Blank
  223. mSpecialGraphicsCharMap['b'] = 0x2409; // Tab
  224. mSpecialGraphicsCharMap['c'] = 0x240C; // Form feed
  225. mSpecialGraphicsCharMap['d'] = 0x240D; // Carriage return
  226. mSpecialGraphicsCharMap['e'] = 0x240A; // Line feed
  227. mSpecialGraphicsCharMap['h'] = 0x2424; // New line
  228. mSpecialGraphicsCharMap['i'] = 0x240B; // Vertical tab/"lantern"
  229. mSpecialGraphicsCharMap['}'] = 0x00A3; // Pound sterling symbol
  230. mSpecialGraphicsCharMap['f'] = 0x00B0; // Degree symbol
  231. mSpecialGraphicsCharMap['`'] = 0x2B25; // Diamond
  232. mSpecialGraphicsCharMap['~'] = 0x2022; // Bullet point
  233. mSpecialGraphicsCharMap['y'] = 0x2264; // Less-than-or-equals sign (<=)
  234. mSpecialGraphicsCharMap['|'] = 0x2260; // Not equals sign (!=)
  235. mSpecialGraphicsCharMap['z'] = 0x2265; // Greater-than-or-equals sign (>=)
  236. mSpecialGraphicsCharMap['g'] = 0x00B1; // Plus-or-minus sign (+/-)
  237. mSpecialGraphicsCharMap['{'] = 0x03C0; // Lowercase Greek letter pi
  238. mSpecialGraphicsCharMap['.'] = 0x25BC; // Down arrow
  239. mSpecialGraphicsCharMap[','] = 0x25C0; // Left arrow
  240. mSpecialGraphicsCharMap['+'] = 0x25B6; // Right arrow
  241. mSpecialGraphicsCharMap['-'] = 0x25B2; // Up arrow
  242. mSpecialGraphicsCharMap['h'] = '#'; // Board of squares
  243. mSpecialGraphicsCharMap['a'] = 0x2592; // Checkerboard
  244. mSpecialGraphicsCharMap['0'] = 0x2588; // Solid block
  245. mSpecialGraphicsCharMap['q'] = 0x2500; // Horizontal line (box drawing)
  246. mSpecialGraphicsCharMap['x'] = 0x2502; // Vertical line (box drawing)
  247. mSpecialGraphicsCharMap['m'] = 0x2514; // Lower left hand corner (box drawing)
  248. mSpecialGraphicsCharMap['j'] = 0x2518; // Lower right hand corner (box drawing)
  249. mSpecialGraphicsCharMap['l'] = 0x250C; // Upper left hand corner (box drawing)
  250. mSpecialGraphicsCharMap['k'] = 0x2510; // Upper right hand corner (box drawing)
  251. mSpecialGraphicsCharMap['w'] = 0x252C; // T pointing downwards (box drawing)
  252. mSpecialGraphicsCharMap['u'] = 0x2524; // T pointing leftwards (box drawing)
  253. mSpecialGraphicsCharMap['t'] = 0x251C; // T pointing rightwards (box drawing)
  254. mSpecialGraphicsCharMap['v'] = 0x2534; // T pointing upwards (box drawing)
  255. mSpecialGraphicsCharMap['n'] = 0x253C; // Large plus/lines crossing (box drawing)
  256. mSpecialGraphicsCharMap['o'] = 0x23BA; // Horizontal scanline 1
  257. mSpecialGraphicsCharMap['p'] = 0x23BB; // Horizontal scanline 3
  258. mSpecialGraphicsCharMap['r'] = 0x23BC; // Horizontal scanline 7
  259. mSpecialGraphicsCharMap['s'] = 0x23BD; // Horizontal scanline 9
  260. }
  261. /**
  262. * Used for moving selection up along with the scrolling text
  263. */
  264. private int mScrollCounter = 0;
  265. /**
  266. * UTF-8 support
  267. */
  268. private static final int UNICODE_REPLACEMENT_CHAR = 0xfffd;
  269. private boolean mDefaultUTF8Mode = false;
  270. private boolean mUTF8Mode = false;
  271. private boolean mUTF8EscapeUsed = false;
  272. private int mUTF8ToFollow = 0;
  273. private ByteBuffer mUTF8ByteBuffer;
  274. private CharBuffer mInputCharBuffer;
  275. private CharsetDecoder mUTF8Decoder;
  276. private UpdateCallback mUTF8ModeNotify;
  277. /**
  278. * Construct a terminal emulator that uses the supplied screen
  279. *
  280. * @param session the terminal session the emulator is attached to
  281. * @param screen the screen to render characters into.
  282. * @param columns the number of columns to emulate
  283. * @param rows the number of rows to emulate
  284. * @param scheme the default color scheme of this emulator
  285. */
  286. public TerminalEmulator(TermSession session, Screen screen, int columns, int rows, ColorScheme scheme) {
  287. mSession = session;
  288. mScreen = screen;
  289. mRows = rows;
  290. mColumns = columns;
  291. mTabStop = new boolean[mColumns];
  292. setColorScheme(scheme);
  293. mUTF8ByteBuffer = ByteBuffer.allocate(4);
  294. mInputCharBuffer = CharBuffer.allocate(2);
  295. mUTF8Decoder = Charset.forName("UTF-8").newDecoder();
  296. mUTF8Decoder.onMalformedInput(CodingErrorAction.REPLACE);
  297. mUTF8Decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
  298. reset();
  299. }
  300. public void updateSize(int columns, int rows) {
  301. if (mRows == rows && mColumns == columns) {
  302. return;
  303. }
  304. if (columns <= 0) {
  305. throw new IllegalArgumentException("rows:" + columns);
  306. }
  307. if (rows <= 0) {
  308. throw new IllegalArgumentException("rows:" + rows);
  309. }
  310. // Try to resize the screen without getting the transcript
  311. int[] cursor = { mCursorCol, mCursorRow };
  312. boolean fastResize = mScreen.fastResize(columns, rows, cursor);
  313. StringBuilder cursorColor = null;
  314. String charAtCursor = null;
  315. StringBuilder colors = null;
  316. String transcriptText = null;
  317. if (!fastResize) {
  318. /* Save the character at the cursor (if one exists) and store an
  319. * ASCII ESC character at the cursor's location
  320. * This is an epic hack that lets us restore the cursor later...
  321. */
  322. cursorColor = new StringBuilder(1);
  323. charAtCursor = mScreen.getSelectedText(cursorColor, mCursorCol, mCursorRow, mCursorCol, mCursorRow);
  324. mScreen.set(mCursorCol, mCursorRow, 27, 0, 0);
  325. colors = new StringBuilder();
  326. transcriptText = mScreen.getTranscriptText(colors);
  327. mScreen.resize(columns, rows, mForeColor, mBackColor);
  328. }
  329. if (mRows != rows) {
  330. mRows = rows;
  331. mTopMargin = 0;
  332. mBottomMargin = mRows;
  333. }
  334. if (mColumns != columns) {
  335. int oldColumns = mColumns;
  336. mColumns = columns;
  337. boolean[] oldTabStop = mTabStop;
  338. mTabStop = new boolean[mColumns];
  339. int toTransfer = Math.min(oldColumns, columns);
  340. System.arraycopy(oldTabStop, 0, mTabStop, 0, toTransfer);
  341. }
  342. if (fastResize) {
  343. // Only need to make sure the cursor is in the right spot
  344. if (cursor[0] >= 0 && cursor[1] >= 0) {
  345. mCursorCol = cursor[0];
  346. mCursorRow = cursor[1];
  347. } else {
  348. // Cursor scrolled off screen, reset the cursor to top left
  349. mCursorCol = 0;
  350. mCursorRow = 0;
  351. }
  352. return;
  353. }
  354. mCursorRow = 0;
  355. mCursorCol = 0;
  356. mAboutToAutoWrap = false;
  357. int newCursorRow = -1;
  358. int newCursorCol = -1;
  359. int newCursorTranscriptPos = -1;
  360. int end = transcriptText.length()-1;
  361. while ((end >= 0) && transcriptText.charAt(end) == '\n') {
  362. end--;
  363. }
  364. char c, cLow;
  365. int foreColor, backColor;
  366. int colorOffset = 0;
  367. for(int i = 0; i <= end; i++) {
  368. c = transcriptText.charAt(i);
  369. foreColor = (colors.charAt(i-colorOffset) >> 4) & 0xf;
  370. backColor = colors.charAt(i-colorOffset) & 0xf;
  371. if (Character.isHighSurrogate(c)) {
  372. cLow = transcriptText.charAt(++i);
  373. emit(Character.toCodePoint(c, cLow), foreColor, backColor);
  374. ++colorOffset;
  375. } else if (c == '\n') {
  376. setCursorCol(0);
  377. doLinefeed();
  378. } else if (c == 27) {
  379. /* We marked the cursor location with ESC earlier, so this
  380. is the place to restore the cursor to */
  381. newCursorRow = mCursorRow;
  382. newCursorCol = mCursorCol;
  383. newCursorTranscriptPos = mScreen.getActiveRows();
  384. if (charAtCursor != null && charAtCursor.length() > 0) {
  385. // Emit the real character that was in this spot
  386. foreColor = (cursorColor.charAt(0) >> 4) & 0xf;
  387. backColor = cursorColor.charAt(0) & 0xf;
  388. emit(charAtCursor.toCharArray(), 0, charAtCursor.length(), foreColor, backColor);
  389. }
  390. } else {
  391. emit(c, foreColor, backColor);
  392. }
  393. }
  394. // If we marked a cursor location, move the cursor there now
  395. if (newCursorRow != -1 && newCursorCol != -1) {
  396. mCursorRow = newCursorRow;
  397. mCursorCol = newCursorCol;
  398. /* Adjust for any scrolling between the time we marked the cursor
  399. location and now */
  400. int scrollCount = mScreen.getActiveRows() - newCursorTranscriptPos;
  401. if (scrollCount > 0 && scrollCount <= newCursorRow) {
  402. mCursorRow -= scrollCount;
  403. } else if (scrollCount > newCursorRow) {
  404. // Cursor scrolled off screen -- reset to top left corner
  405. mCursorRow = 0;
  406. mCursorCol = 0;
  407. }
  408. }
  409. }
  410. /**
  411. * Get the cursor's current row.
  412. *
  413. * @return the cursor's current row.
  414. */
  415. public final int getCursorRow() {
  416. return mCursorRow;
  417. }
  418. /**
  419. * Get the cursor's current column.
  420. *
  421. * @return the cursor's current column.
  422. */
  423. public final int getCursorCol() {
  424. return mCursorCol;
  425. }
  426. public final boolean getKeypadApplicationMode() {
  427. return mbKeypadApplicationMode;
  428. }
  429. private void setDefaultTabStops() {
  430. for (int i = 0; i < mColumns; i++) {
  431. mTabStop[i] = (i & 7) == 0 && i != 0;
  432. }
  433. }
  434. /**
  435. * Accept bytes (typically from the pseudo-teletype) and process them.
  436. *
  437. * @param buffer a byte array containing the bytes to be processed
  438. * @param base the first index of the array to process
  439. * @param length the number of bytes in the array to process
  440. */
  441. public void append(byte[] buffer, int base, int length) {
  442. for (int i = 0; i < length; i++) {
  443. byte b = buffer[base + i];
  444. try {
  445. if (EmulatorDebug.LOG_CHARACTERS_FLAG) {
  446. char printableB = (char) b;
  447. if (b < 32 || b > 126) {
  448. printableB = ' ';
  449. }
  450. Log.w(EmulatorDebug.LOG_TAG, "'" + Character.toString(printableB)
  451. + "' (" + Integer.toString(b) + ")");
  452. }
  453. process(b);
  454. mProcessedCharCount++;
  455. } catch (Exception e) {
  456. Log.e(EmulatorDebug.LOG_TAG, "Exception while processing character "
  457. + Integer.toString(mProcessedCharCount) + " code "
  458. + Integer.toString(b), e);
  459. }
  460. }
  461. }
  462. private void process(byte b) {
  463. process(b, true);
  464. }
  465. private void process(byte b, boolean doUTF8) {
  466. // Let the UTF-8 decoder try to handle it if we're in UTF-8 mode
  467. if (doUTF8 && mUTF8Mode && handleUTF8Sequence(b)) {
  468. return;
  469. }
  470. // Handle C1 control characters
  471. if ((b & 0x80) == 0x80 && (b & 0x7f) <= 0x1f) {
  472. /* ESC ((code & 0x7f) + 0x40) is the two-byte escape sequence
  473. corresponding to a particular C1 code */
  474. startEscapeSequence(ESC);
  475. process((byte) ((b & 0x7f) + 0x40), false);
  476. return;
  477. }
  478. switch (b) {
  479. case 0: // NUL
  480. // Do nothing
  481. break;
  482. case 7: // BEL
  483. // Do nothing
  484. break;
  485. case 8: // BS
  486. setCursorCol(Math.max(0, mCursorCol - 1));
  487. break;
  488. case 9: // HT
  489. // Move to next tab stop, but not past edge of screen
  490. setCursorCol(nextTabStop(mCursorCol));
  491. break;
  492. case 13:
  493. setCursorCol(0);
  494. break;
  495. case 10: // CR
  496. case 11: // VT
  497. case 12: // LF
  498. doLinefeed();
  499. break;
  500. case 14: // SO:
  501. setAltCharSet(true);
  502. break;
  503. case 15: // SI:
  504. setAltCharSet(false);
  505. break;
  506. case 24: // CAN
  507. case 26: // SUB
  508. if (mEscapeState != ESC_NONE) {
  509. mEscapeState = ESC_NONE;
  510. emit((byte) 127);
  511. }
  512. break;
  513. case 27: // ESC
  514. // Always starts an escape sequence
  515. startEscapeSequence(ESC);
  516. break;
  517. default:
  518. mContinueSequence = false;
  519. switch (mEscapeState) {
  520. case ESC_NONE:
  521. if (b >= 32) {
  522. emit(b);
  523. }
  524. break;
  525. case ESC:
  526. doEsc(b);
  527. break;
  528. case ESC_POUND:
  529. doEscPound(b);
  530. break;
  531. case ESC_SELECT_LEFT_PAREN:
  532. doEscSelectLeftParen(b);
  533. break;
  534. case ESC_SELECT_RIGHT_PAREN:
  535. doEscSelectRightParen(b);
  536. break;
  537. case ESC_LEFT_SQUARE_BRACKET:
  538. doEscLeftSquareBracket(b);
  539. break;
  540. case ESC_LEFT_SQUARE_BRACKET_QUESTION_MARK:
  541. doEscLSBQuest(b);
  542. break;
  543. case ESC_PERCENT:
  544. doEscPercent(b);
  545. break;
  546. default:
  547. unknownSequence(b);
  548. break;
  549. }
  550. if (!mContinueSequence) {
  551. mEscapeState = ESC_NONE;
  552. }
  553. break;
  554. }
  555. }
  556. private boolean handleUTF8Sequence(byte b) {
  557. if (mUTF8ToFollow == 0 && (b & 0x80) == 0) {
  558. // ASCII character -- we don't need to handle this
  559. return false;
  560. }
  561. if (mUTF8ToFollow > 0) {
  562. if ((b & 0xc0) != 0x80) {
  563. /* Not a UTF-8 continuation byte (doesn't begin with 0b10)
  564. Replace the entire sequence with the replacement char */
  565. mUTF8ToFollow = 0;
  566. mUTF8ByteBuffer.clear();
  567. emit(UNICODE_REPLACEMENT_CHAR);
  568. return true;
  569. }
  570. mUTF8ByteBuffer.put(b);
  571. if (--mUTF8ToFollow == 0) {
  572. // Sequence complete -- decode and emit it
  573. ByteBuffer byteBuf = mUTF8ByteBuffer;
  574. CharBuffer charBuf = mInputCharBuffer;
  575. CharsetDecoder decoder = mUTF8Decoder;
  576. byteBuf.rewind();
  577. decoder.reset();
  578. decoder.decode(byteBuf, charBuf, true);
  579. decoder.flush(charBuf);
  580. char[] chars = charBuf.array();
  581. if (chars[0] >= 0x80 && chars[0] <= 0x9f) {
  582. /* Sequence decoded to a C1 control character which needs
  583. to be sent through process() again */
  584. process((byte) chars[0], false);
  585. } else {
  586. emit(chars);
  587. }
  588. byteBuf.clear();
  589. charBuf.clear();
  590. }
  591. } else {
  592. if ((b & 0xe0) == 0xc0) { // 0b110 -- two-byte sequence
  593. mUTF8ToFollow = 1;
  594. } else if ((b & 0xf0) == 0xe0) { // 0b1110 -- three-byte sequence
  595. mUTF8ToFollow = 2;
  596. } else if ((b & 0xf8) == 0xf0) { // 0b11110 -- four-byte sequence
  597. mUTF8ToFollow = 3;
  598. } else {
  599. // Not a valid UTF-8 sequence start -- replace this char
  600. emit(UNICODE_REPLACEMENT_CHAR);
  601. return true;
  602. }
  603. mUTF8ByteBuffer.put(b);
  604. }
  605. return true;
  606. }
  607. private void setAltCharSet(boolean alternateCharSet) {
  608. mAlternateCharSet = alternateCharSet;
  609. }
  610. private int nextTabStop(int cursorCol) {
  611. for (int i = cursorCol + 1; i < mColumns; i++) {
  612. if (mTabStop[i]) {
  613. return i;
  614. }
  615. }
  616. return mColumns - 1;
  617. }
  618. private int prevTabStop(int cursorCol) {
  619. for (int i = cursorCol - 1; i >= 0; i--) {
  620. if (mTabStop[i]) {
  621. return i;
  622. }
  623. }
  624. return 0;
  625. }
  626. private void doEscPercent(byte b) {
  627. switch (b) {
  628. case '@': // Esc % @ -- return to ISO 2022 mode
  629. setUTF8Mode(false);
  630. mUTF8EscapeUsed = true;
  631. break;
  632. case 'G': // Esc % G -- UTF-8 mode
  633. setUTF8Mode(true);
  634. mUTF8EscapeUsed = true;
  635. break;
  636. default: // unimplemented character set
  637. break;
  638. }
  639. }
  640. private void doEscLSBQuest(byte b) {
  641. int mask = getDecFlagsMask(getArg0(0));
  642. switch (b) {
  643. case 'h': // Esc [ ? Pn h - DECSET
  644. mDecFlags |= mask;
  645. break;
  646. case 'l': // Esc [ ? Pn l - DECRST
  647. mDecFlags &= ~mask;
  648. break;
  649. case 'r': // Esc [ ? Pn r - restore
  650. mDecFlags = (mDecFlags & ~mask) | (mSavedDecFlags & mask);
  651. break;
  652. case 's': // Esc [ ? Pn s - save
  653. mSavedDecFlags = (mSavedDecFlags & ~mask) | (mDecFlags & mask);
  654. break;
  655. default:
  656. parseArg(b);
  657. break;
  658. }
  659. // 132 column mode
  660. if ((mask & K_132_COLUMN_MODE_MASK) != 0) {
  661. // We don't actually set 132 cols, but we do want the
  662. // side effect of clearing the screen and homing the cursor.
  663. blockClear(0, 0, mColumns, mRows);
  664. setCursorRowCol(0, 0);
  665. }
  666. // origin mode
  667. if ((mask & K_ORIGIN_MODE_MASK) != 0) {
  668. // Home the cursor.
  669. setCursorPosition(0, 0);
  670. }
  671. }
  672. private int getDecFlagsMask(int argument) {
  673. if (argument >= 1 && argument <= 9) {
  674. return (1 << argument);
  675. }
  676. return 0;
  677. }
  678. private void startEscapeSequence(int escapeState) {
  679. mEscapeState = escapeState;
  680. mArgIndex = 0;
  681. for (int j = 0; j < MAX_ESCAPE_PARAMETERS; j++) {
  682. mArgs[j] = -1;
  683. }
  684. }
  685. private void doLinefeed() {
  686. int newCursorRow = mCursorRow + 1;
  687. if (newCursorRow >= mBottomMargin) {
  688. scroll();
  689. newCursorRow = mBottomMargin - 1;
  690. }
  691. setCursorRow(newCursorRow);
  692. }
  693. private void continueSequence() {
  694. mContinueSequence = true;
  695. }
  696. private void continueSequence(int state) {
  697. mEscapeState = state;
  698. mContinueSequence = true;
  699. }
  700. private void doEscSelectLeftParen(byte b) {
  701. doSelectCharSet(true, b);
  702. }
  703. private void doEscSelectRightParen(byte b) {
  704. doSelectCharSet(false, b);
  705. }
  706. private void doSelectCharSet(boolean isG0CharSet, byte b) {
  707. switch (b) {
  708. case 'A': // United Kingdom character set
  709. break;
  710. case 'B': // ASCII set
  711. break;
  712. case '0': // Special Graphics
  713. break;
  714. case '1': // Alternate character set
  715. break;
  716. case '2':
  717. break;
  718. default:
  719. unknownSequence(b);
  720. }
  721. }
  722. private void doEscPound(byte b) {
  723. switch (b) {
  724. case '8': // Esc # 8 - DECALN alignment test
  725. mScreen.blockSet(0, 0, mColumns, mRows, 'E',
  726. getForeColor(), getBackColor());
  727. break;
  728. default:
  729. unknownSequence(b);
  730. break;
  731. }
  732. }
  733. private void doEsc(byte b) {
  734. switch (b) {
  735. case '#':
  736. continueSequence(ESC_POUND);
  737. break;
  738. case '(':
  739. continueSequence(ESC_SELECT_LEFT_PAREN);
  740. break;
  741. case ')':
  742. continueSequence(ESC_SELECT_RIGHT_PAREN);
  743. break;
  744. case '7': // DECSC save cursor
  745. mSavedCursorRow = mCursorRow;
  746. mSavedCursorCol = mCursorCol;
  747. break;
  748. case '8': // DECRC restore cursor
  749. setCursorRowCol(mSavedCursorRow, mSavedCursorCol);
  750. break;
  751. case 'D': // INDEX
  752. doLinefeed();
  753. break;
  754. case 'E': // NEL
  755. setCursorCol(0);
  756. doLinefeed();
  757. break;
  758. case 'F': // Cursor to lower-left corner of screen
  759. setCursorRowCol(0, mBottomMargin - 1);
  760. break;
  761. case 'H': // Tab set
  762. mTabStop[mCursorCol] = true;
  763. break;
  764. case 'M': // Reverse index
  765. if (mCursorRow <= mTopMargin) {
  766. mScreen.blockCopy(0, mTopMargin, mColumns, mBottomMargin
  767. - (mTopMargin + 1), 0, mTopMargin + 1);
  768. blockClear(0, mTopMargin, mColumns);
  769. } else {
  770. mCursorRow--;
  771. }
  772. break;
  773. case 'N': // SS2
  774. unimplementedSequence(b);
  775. break;
  776. case '0': // SS3
  777. unimplementedSequence(b);
  778. break;
  779. case 'P': // Device control string
  780. unimplementedSequence(b);
  781. break;
  782. case 'Z': // return terminal ID
  783. sendDeviceAttributes();
  784. break;
  785. case '[':
  786. continueSequence(ESC_LEFT_SQUARE_BRACKET);
  787. break;
  788. case '=': // DECKPAM
  789. mbKeypadApplicationMode = true;
  790. break;
  791. case '>' : // DECKPNM
  792. mbKeypadApplicationMode = false;
  793. break;
  794. default:
  795. unknownSequence(b);
  796. break;
  797. }
  798. }
  799. private void doEscLeftSquareBracket(byte b) {
  800. switch (b) {
  801. case '@': // ESC [ Pn @ - ICH Insert Characters
  802. {
  803. int charsAfterCursor = mColumns - mCursorCol;
  804. int charsToInsert = Math.min(getArg0(1), charsAfterCursor);
  805. int charsToMove = charsAfterCursor - charsToInsert;
  806. mScreen.blockCopy(mCursorCol, mCursorRow, charsToMove, 1,
  807. mCursorCol + charsToInsert, mCursorRow);
  808. blockClear(mCursorCol, mCursorRow, charsToInsert);
  809. }
  810. break;
  811. case 'A': // ESC [ Pn A - Cursor Up
  812. setCursorRow(Math.max(mTopMargin, mCursorRow - getArg0(1)));
  813. break;
  814. case 'B': // ESC [ Pn B - Cursor Down
  815. setCursorRow(Math.min(mBottomMargin - 1, mCursorRow + getArg0(1)));
  816. break;
  817. case 'C': // ESC [ Pn C - Cursor Right
  818. setCursorCol(Math.min(mColumns - 1, mCursorCol + getArg0(1)));
  819. break;
  820. case 'D': // ESC [ Pn D - Cursor Left
  821. setCursorCol(Math.max(0, mCursorCol - getArg0(1)));
  822. break;
  823. case 'G': // ESC [ Pn G - Cursor Horizontal Absolute
  824. setCursorCol(Math.min(Math.max(1, getArg0(1)), mColumns) - 1);
  825. break;
  826. case 'H': // ESC [ Pn ; H - Cursor Position
  827. setHorizontalVerticalPosition();
  828. break;
  829. case 'J': // ESC [ Pn J - Erase in Display
  830. switch (getArg0(0)) {
  831. case 0: // Clear below
  832. blockClear(mCursorCol, mCursorRow, mColumns - mCursorCol);
  833. blockClear(0, mCursorRow + 1, mColumns,
  834. mBottomMargin - (mCursorRow + 1));
  835. break;
  836. case 1: // Erase from the start of the screen to the cursor.
  837. blockClear(0, mTopMargin, mColumns, mCursorRow - mTopMargin);
  838. blockClear(0, mCursorRow, mCursorCol + 1);
  839. break;
  840. case 2: // Clear all
  841. blockClear(0, mTopMargin, mColumns, mBottomMargin - mTopMargin);
  842. break;
  843. default:
  844. unknownSequence(b);
  845. break;
  846. }
  847. break;
  848. case 'K': // ESC [ Pn K - Erase in Line
  849. switch (getArg0(0)) {
  850. case 0: // Clear to right
  851. blockClear(mCursorCol, mCursorRow, mColumns - mCursorCol);
  852. break;
  853. case 1: // Erase start of line to cursor (including cursor)
  854. blockClear(0, mCursorRow, mCursorCol + 1);
  855. break;
  856. case 2: // Clear whole line
  857. blockClear(0, mCursorRow, mColumns);
  858. break;
  859. default:
  860. unknownSequence(b);
  861. break;
  862. }
  863. break;
  864. case 'L': // Insert Lines
  865. {
  866. int linesAfterCursor = mBottomMargin - mCursorRow;
  867. int linesToInsert = Math.min(getArg0(1), linesAfterCursor);
  868. int linesToMove = linesAfterCursor - linesToInsert;
  869. mScreen.blockCopy(0, mCursorRow, mColumns, linesToMove, 0,
  870. mCursorRow + linesToInsert);
  871. blockClear(0, mCursorRow, mColumns, linesToInsert);
  872. }
  873. break;
  874. case 'M': // Delete Lines
  875. {
  876. int linesAfterCursor = mBottomMargin - mCursorRow;
  877. int linesToDelete = Math.min(getArg0(1), linesAfterCursor);
  878. int linesToMove = linesAfterCursor - linesToDelete;
  879. mScreen.blockCopy(0, mCursorRow + linesToDelete, mColumns,
  880. linesToMove, 0, mCursorRow);
  881. blockClear(0, mCursorRow + linesToMove, mColumns, linesToDelete);
  882. }
  883. break;
  884. case 'P': // Delete Characters
  885. {
  886. int charsAfterCursor = mColumns - mCursorCol;
  887. int charsToDelete = Math.min(getArg0(1), charsAfterCursor);
  888. int charsToMove = charsAfterCursor - charsToDelete;
  889. mScreen.blockCopy(mCursorCol + charsToDelete, mCursorRow,
  890. charsToMove, 1, mCursorCol, mCursorRow);
  891. blockClear(mCursorCol + charsToMove, mCursorRow, charsToDelete);
  892. }
  893. break;
  894. case 'T': // Mouse tracking
  895. unimplementedSequence(b);
  896. break;
  897. case 'X': // Erase characters
  898. blockClear(mCursorCol, mCursorRow, getArg0(0));
  899. break;
  900. case 'Z': // Back tab
  901. setCursorCol(prevTabStop(mCursorCol));
  902. break;
  903. case '?': // Esc [ ? -- start of a private mode set
  904. continueSequence(ESC_LEFT_SQUARE_BRACKET_QUESTION_MARK);
  905. break;
  906. case 'c': // Send device attributes
  907. sendDeviceAttributes();
  908. break;
  909. case 'd': // ESC [ Pn d - Vert Position Absolute
  910. setCursorRow(Math.min(Math.max(1, getArg0(1)), mRows) - 1);
  911. break;
  912. case 'f': // Horizontal and Vertical Position
  913. setHorizontalVerticalPosition();
  914. break;
  915. case 'g': // Clear tab stop
  916. switch (getArg0(0)) {
  917. case 0:
  918. mTabStop[mCursorCol] = false;
  919. break;
  920. case 3:
  921. for (int i = 0; i < mColumns; i++) {
  922. mTabStop[i] = false;
  923. }
  924. break;
  925. default:
  926. // Specified to have no effect.
  927. break;
  928. }
  929. break;
  930. case 'h': // Set Mode
  931. doSetMode(true);
  932. break;
  933. case 'l': // Reset Mode
  934. doSetMode(false);
  935. break;
  936. case 'm': // Esc [ Pn m - character attributes.
  937. selectGraphicRendition();
  938. break;
  939. case 'r': // Esc [ Pn ; Pn r - set top and bottom margins
  940. {
  941. // The top margin defaults to 1, the bottom margin
  942. // (unusually for arguments) defaults to mRows.
  943. //
  944. // The escape sequence numbers top 1..23, but we
  945. // number top 0..22.
  946. // The escape sequence numbers bottom 2..24, and
  947. // so do we (because we use a zero based numbering
  948. // scheme, but we store the first line below the
  949. // bottom-most scrolling line.
  950. // As a result, we adjust the top line by -1, but
  951. // we leave the bottom line alone.
  952. //
  953. // Also require that top + 2 <= bottom
  954. int top = Math.max(0, Math.min(getArg0(1) - 1, mRows - 2));
  955. int bottom = Math.max(top + 2, Math.min(getArg1(mRows), mRows));
  956. mTopMargin = top;
  957. mBottomMargin = bottom;
  958. // The cursor is placed in the home position
  959. setCursorRowCol(mTopMargin, 0);
  960. }
  961. break;
  962. default:
  963. parseArg(b);
  964. break;
  965. }
  966. }
  967. private void selectGraphicRendition() {
  968. for (int i = 0; i <= mArgIndex; i++) {
  969. int code = mArgs[i];
  970. if ( code < 0) {
  971. if (mArgIndex > 0) {
  972. continue;
  973. } else {
  974. code = 0;
  975. }
  976. }
  977. // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
  978. if (code == 0) { // reset
  979. mInverseColors = false;
  980. mForeColor = mDefaultForeColor;
  981. mBackColor = mDefaultBackColor;
  982. } else if (code == 1) { // bold
  983. mForeColor |= 0x8;
  984. } else if (code == 3) { // italics, but rarely used as such; "standout" (inverse colors) with TERM=screen
  985. mInverseColors = true;
  986. } else if (code == 4) { // underscore
  987. mBackColor |= 0x8;
  988. } else if (code == 7) { // inverse
  989. mInverseColors = true;
  990. } else if (code == 10) { // exit alt charset (TERM=linux)
  991. setAltCharSet(false);
  992. } else if (code == 11) { // enter alt charset (TERM=linux)
  993. setAltCharSet(true);
  994. } else if (code == 22) { // Normal color or intensity, neither bright, bold nor faint
  995. mForeColor &= 0x7;
  996. } else if (code == 23) { // not italic, but rarely used as such; clears standout with TERM=screen
  997. mInverseColors = false;
  998. } else if (code == 24) { // underline: none
  999. mBackColor &= 0x7;
  1000. } else if (code == 27) { // image: positive
  1001. mInverseColors = false;
  1002. } else if (code >= 30 && code <= 37) { // foreground color
  1003. mForeColor = (mForeColor & 0x8) | (code - 30);
  1004. } else if (code == 39) { // set default text color
  1005. mForeColor = mDefaultForeColor | (mForeColor & 0x8); // preserve bold
  1006. mBackColor = mBackColor & 0x7; // no underline
  1007. } else if (code >= 40 && code <= 47) { // background color
  1008. mBackColor = (mBackColor & 0x8) | (code - 40);
  1009. } else if (code == 49) { // set default background color
  1010. mBackColor = mDefaultBackColor | (mBackColor & 0x8); // preserve underscore.
  1011. } else {
  1012. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1013. Log.w(EmulatorDebug.LOG_TAG, String.format("SGR unknown code %d", code));
  1014. }
  1015. }
  1016. }
  1017. }
  1018. private void blockClear(int sx, int sy, int w) {
  1019. blockClear(sx, sy, w, 1);
  1020. }
  1021. private void blockClear(int sx, int sy, int w, int h) {
  1022. mScreen.blockSet(sx, sy, w, h, ' ', getForeColor(), getBackColor());
  1023. }
  1024. private int getForeColor() {
  1025. return mInverseColors ?
  1026. ((mBackColor & 0x7) | (mForeColor & 0x8)) : mForeColor;
  1027. }
  1028. private int getBackColor() {
  1029. return mInverseColors ?
  1030. ((mForeColor & 0x7) | (mBackColor & 0x8)) : mBackColor;
  1031. }
  1032. private void doSetMode(boolean newValue) {
  1033. int modeBit = getArg0(0);
  1034. switch (modeBit) {
  1035. case 4:
  1036. mInsertMode = newValue;
  1037. break;
  1038. case 20:
  1039. mAutomaticNewlineMode = newValue;
  1040. break;
  1041. default:
  1042. unknownParameter(modeBit);
  1043. break;
  1044. }
  1045. }
  1046. private void setHorizontalVerticalPosition() {
  1047. // Parameters are Row ; Column
  1048. setCursorPosition(getArg1(1) - 1, getArg0(1) - 1);
  1049. }
  1050. private void setCursorPosition(int x, int y) {
  1051. int effectiveTopMargin = 0;
  1052. int effectiveBottomMargin = mRows;
  1053. if ((mDecFlags & K_ORIGIN_MODE_MASK) != 0) {
  1054. effectiveTopMargin = mTopMargin;
  1055. effectiveBottomMargin = mBottomMargin;
  1056. }
  1057. int newRow =
  1058. Math.max(effectiveTopMargin, Math.min(effectiveTopMargin + y,
  1059. effectiveBottomMargin - 1));
  1060. int newCol = Math.max(0, Math.min(x, mColumns - 1));
  1061. setCursorRowCol(newRow, newCol);
  1062. }
  1063. private void sendDeviceAttributes() {
  1064. // This identifies us as a DEC vt100 with advanced
  1065. // video options. This is what the xterm terminal
  1066. // emulator sends.
  1067. byte[] attributes =
  1068. {
  1069. /* VT100 */
  1070. (byte) 27, (byte) '[', (byte) '?', (byte) '1',
  1071. (byte) ';', (byte) '2', (byte) 'c'
  1072. /* VT220
  1073. (byte) 27, (byte) '[', (byte) '?', (byte) '6',
  1074. (byte) '0', (byte) ';',
  1075. (byte) '1', (byte) ';',
  1076. (byte) '2', (byte) ';',
  1077. (byte) '6', (byte) ';',
  1078. (byte) '8', (byte) ';',
  1079. (byte) '9', (byte) ';',
  1080. (byte) '1', (byte) '5', (byte) ';',
  1081. (byte) 'c'
  1082. */
  1083. };
  1084. mSession.write(attributes, 0, attributes.length);
  1085. }
  1086. private void scroll() {
  1087. //System.out.println("Scroll(): mTopMargin " + mTopMargin + " mBottomMargin " + mBottomMargin);
  1088. mScrollCounter ++;
  1089. mScreen.scroll(mTopMargin, mBottomMargin);
  1090. }
  1091. /**
  1092. * Process the next ASCII character of a parameter.
  1093. *
  1094. * @param b The next ASCII character of the paramater sequence.
  1095. */
  1096. private void parseArg(byte b) {
  1097. if (b >= '0' && b <= '9') {
  1098. if (mArgIndex < mArgs.length) {
  1099. int oldValue = mArgs[mArgIndex];
  1100. int thisDigit = b - '0';
  1101. int value;
  1102. if (oldValue >= 0) {
  1103. value = oldValue * 10 + thisDigit;
  1104. } else {
  1105. value = thisDigit;
  1106. }
  1107. mArgs[mArgIndex] = value;
  1108. }
  1109. continueSequence();
  1110. } else if (b == ';') {
  1111. if (mArgIndex < mArgs.length) {
  1112. mArgIndex++;
  1113. }
  1114. continueSequence();
  1115. } else {
  1116. unknownSequence(b);
  1117. }
  1118. }
  1119. private int getArg0(int defaultValue) {
  1120. return getArg(0, defaultValue);
  1121. }
  1122. private int getArg1(int defaultValue) {
  1123. return getArg(1, defaultValue);
  1124. }
  1125. private int getArg(int index, int defaultValue) {
  1126. int result = mArgs[index];
  1127. if (result < 0) {
  1128. result = defaultValue;
  1129. }
  1130. return result;
  1131. }
  1132. private void unimplementedSequence(byte b) {
  1133. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1134. logError("unimplemented", b);
  1135. }
  1136. finishSequence();
  1137. }
  1138. private void unknownSequence(byte b) {
  1139. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1140. logError("unknown", b);
  1141. }
  1142. finishSequence();
  1143. }
  1144. private void unknownParameter(int parameter) {
  1145. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1146. StringBuilder buf = new StringBuilder();
  1147. buf.append("Unknown parameter");
  1148. buf.append(parameter);
  1149. logError(buf.toString());
  1150. }
  1151. }
  1152. private void logError(String errorType, byte b) {
  1153. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1154. StringBuilder buf = new StringBuilder();
  1155. buf.append(errorType);
  1156. buf.append(" sequence ");
  1157. buf.append(" EscapeState: ");
  1158. buf.append(mEscapeState);
  1159. buf.append(" char: '");
  1160. buf.append((char) b);
  1161. buf.append("' (");
  1162. buf.append(b);
  1163. buf.append(")");
  1164. boolean firstArg = true;
  1165. for (int i = 0; i <= mArgIndex; i++) {
  1166. int value = mArgs[i];
  1167. if (value >= 0) {
  1168. if (firstArg) {
  1169. firstArg = false;
  1170. buf.append("args = ");
  1171. }
  1172. buf.append(String.format("%d; ", value));
  1173. }
  1174. }
  1175. logError(buf.toString());
  1176. }
  1177. }
  1178. private void logError(String error) {
  1179. if (EmulatorDebug.LOG_UNKNOWN_ESCAPE_SEQUENCES) {
  1180. Log.e(EmulatorDebug.LOG_TAG, error);
  1181. }
  1182. finishSequence();
  1183. }
  1184. private void finishSequence() {
  1185. mEscapeState = ESC_NONE;
  1186. }
  1187. private boolean autoWrapEnabled() {
  1188. // Always enable auto wrap, because it's useful on a small screen
  1189. return true;
  1190. // return (mDecFlags & K_WRAPAROUND_MODE_MASK) != 0;
  1191. }
  1192. /**
  1193. * Send a Unicode code point to the screen.
  1194. *
  1195. * @param c The code point of the character to display
  1196. * @param foreColor The foreground color of the character
  1197. * @param backColor The background color of the character
  1198. */
  1199. private void emit(int c, int foreColor, int backColor) {
  1200. boolean autoWrap = autoWrapEnabled();
  1201. int width = UnicodeTranscript.charWidth(c);
  1202. if (autoWrap) {
  1203. if (mCursorCol == mColumns - 1 && (mAboutToAutoWrap || width == 2)) {
  1204. mScreen.setLineWrap(mCursorRow);
  1205. mCursorCol = 0;
  1206. mJustWrapped = true;
  1207. if (mCursorRow + 1 < mBottomMargin) {
  1208. mCursorRow++;
  1209. } else {
  1210. scroll();
  1211. }
  1212. }
  1213. }
  1214. if (mInsertMode & width != 0) { // Move character to right one space
  1215. int destCol = mCursorCol + width;
  1216. if (destCol < mColumns) {
  1217. mScreen.blockCopy(mCursorCol, mCursorRow, mColumns - destCol,
  1218. 1, destCol, mCursorRow);
  1219. }
  1220. }
  1221. if (width == 0) {
  1222. // Combining character -- store along with character it modifies
  1223. if (mJustWrapped) {
  1224. mScreen.set(mColumns - mLastEmittedCharWidth, mCursorRow - 1, c, foreColor, backColor);
  1225. } else {
  1226. mScreen.set(mCursorCol - mLastEmittedCharWidth, mCursorRow, c, foreColor, backColor);
  1227. }
  1228. } else {
  1229. mScreen.set(mCursorCol, mCursorRow, c, foreColor, backColor);
  1230. mJustWrapped = false;
  1231. }
  1232. if (autoWrap) {
  1233. mAboutToAutoWrap = (mCursorCol == mColumns - 1);
  1234. }
  1235. mCursorCol = Math.min(mCursorCol + width, mColumns - 1);
  1236. if (width > 0) {
  1237. mLastEmittedCharWidth = width;
  1238. }
  1239. }
  1240. private void emit(int c) {
  1241. emit(c, getForeColor(), getBackColor());
  1242. }
  1243. private void emit(byte b) {
  1244. if (mAlternateCharSet && b < 128) {
  1245. emit((int) mSpecialGraphicsCharMap[b]);
  1246. } else {
  1247. emit((int) b);
  1248. }
  1249. }
  1250. /**
  1251. * Send a UTF-16 char or surrogate pair to the screen.
  1252. *
  1253. * @param c A char[2] containing either a single UTF-16 char or a surrogate pair to be sent to the screen.
  1254. */
  1255. private void emit(char[] c) {
  1256. if (Character.isHighSurrogate(c[0])) {
  1257. emit(Character.toCodePoint(c[0], c[1]));
  1258. } else {
  1259. emit((int) c[0]);
  1260. }
  1261. }
  1262. /**
  1263. * Send an array of UTF-16 chars to the screen.
  1264. *
  1265. * @param c A char[] array whose contents are to be sent to the screen.
  1266. */
  1267. private void emit(char[] c, int offset, int length, int foreColor, int backColor) {
  1268. for (int i = offset; i < length; ++i) {
  1269. if (c[i] == 0) {
  1270. break;
  1271. }
  1272. if (Character.isHighSurrogate(c[i])) {
  1273. emit(Character.toCodePoint(c[i], c[i+1]), foreColor, backColor);
  1274. ++i;
  1275. } else {
  1276. emit((int) c[i], foreColor, backColor);
  1277. }
  1278. }
  1279. }
  1280. private void emit(char[] c, int offset, int length) {
  1281. emit(c, offset, length, getForeColor(), getBackColor());
  1282. }
  1283. private void setCursorRow(int row) {
  1284. mCursorRow = row;
  1285. mAboutToAutoWrap = false;
  1286. }
  1287. private void setCursorCol(int col) {
  1288. mCursorCol = col;
  1289. mAboutToAutoWrap = false;
  1290. }
  1291. private void setCursorRowCol(int row, int col) {
  1292. mCursorRow = Math.min(row, mRows-1);
  1293. mCursorCol = Math.min(col, mColumns-1);
  1294. mAboutToAutoWrap = false;
  1295. }
  1296. public int getScrollCounter() {
  1297. return mScrollCounter;
  1298. }
  1299. public void clearScrollCounter() {
  1300. mScrollCounter = 0;
  1301. }
  1302. /**
  1303. * Reset the terminal emulator to its initial state.
  1304. */
  1305. public void reset() {
  1306. mCursorRow = 0;
  1307. mCursorCol = 0;
  1308. mArgIndex = 0;
  1309. mContinueSequence = false;
  1310. mEscapeState = ESC_NONE;
  1311. mSavedCursorRow = 0;
  1312. mSavedCursorCol = 0;
  1313. mDecFlags = 0;
  1314. mSavedDecFlags = 0;
  1315. mInsertMode = false;
  1316. mAutomaticNewlineMode = false;
  1317. mTopMargin = 0;
  1318. mBottomMargin = mRows;
  1319. mAboutToAutoWrap = false;
  1320. mForeColor = mDefaultForeColor;
  1321. mBackColor = mDefaultBackColor;
  1322. mInverseColors = false;
  1323. mbKeypadApplicationMode = false;
  1324. mAlternateCharSet = false;
  1325. // mProcessedCharCount is preserved unchanged.
  1326. setDefaultTabStops();
  1327. blockClear(0, 0, mColumns, mRows);
  1328. setUTF8Mode(mDefaultUTF8Mode);
  1329. mUTF8EscapeUsed = false;
  1330. mUTF8ToFollow = 0;
  1331. mUTF8ByteBuffer.clear();
  1332. mInputCharBuffer.clear();
  1333. }
  1334. public void setDefaultUTF8Mode(boolean defaultToUTF8Mode) {
  1335. mDefaultUTF8Mode = defaultToUTF8Mode;
  1336. if (!mUTF8EscapeUsed) {
  1337. setUTF8Mode(defaultToUTF8Mode);
  1338. }
  1339. }
  1340. public void setUTF8Mode(boolean utf8Mode) {
  1341. if (utf8Mode && !mUTF8Mode) {
  1342. mUTF8ToFollow = 0;
  1343. mUTF8ByteBuffer.clear();
  1344. mInp