X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Ftterminal%2FECMA48.java;h=c277795d69450b2db44f79d77a1caa8062c11275;hb=955c55b766a8aebb528d5af5f7582a857c72e2f5;hp=f9c98661b83c65a9329582ea12092cae2ea5db2f;hpb=3af53a35f41caa36050a69d39a8ec40be92e7aca;p=fanfix.git diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index f9c9866..c277795 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -52,6 +52,8 @@ import jexer.bits.Color; import jexer.bits.Cell; import jexer.bits.CellAttributes; import jexer.bits.StringUtils; +import jexer.event.TInputEvent; +import jexer.event.TKeypressEvent; import jexer.event.TMouseEvent; import jexer.io.ReadTimeoutException; import jexer.io.TimeoutInputStream; @@ -348,7 +350,7 @@ public class ECMA48 implements Runnable { /** * Last character printed. */ - private char repCh; + private int repCh; /** * VT100-style line wrapping: a character is placed in column 80 (or @@ -490,6 +492,12 @@ public class ECMA48 implements Runnable { */ private GlyphMaker glyphMaker = null; + /** + * Input queue for keystrokes and mouse events to send to the remote + * side. + */ + private ArrayList userQueue = new ArrayList(); + /** * DECSC/DECRC save/restore a subset of the total state. This class * encapsulates those specific flags/modes. @@ -682,12 +690,18 @@ public class ECMA48 implements Runnable { char [] readBufferUTF8 = null; byte [] readBuffer = null; if (utf8) { - readBufferUTF8 = new char[128]; + readBufferUTF8 = new char[2048]; } else { - readBuffer = new byte[128]; + readBuffer = new byte[2048]; } while (!done && !stopReaderThread) { + synchronized (userQueue) { + while (userQueue.size() > 0) { + handleUserEvent(userQueue.remove(0)); + } + } + try { int n = inputStream.available(); @@ -709,7 +723,7 @@ public class ECMA48 implements Runnable { } if (n == 0) { try { - Thread.sleep(2); + Thread.sleep(10); } catch (InterruptedException e) { // SQUASH } @@ -736,15 +750,17 @@ public class ECMA48 implements Runnable { } else { // Don't step on UI events synchronized (this) { - for (int i = 0; i < rc; i++) { - int ch = 0; - if (utf8) { - ch = readBufferUTF8[i]; - } else { - ch = readBuffer[i]; + if (utf8) { + for (int i = 0; i < rc;) { + int ch = Character.codePointAt(readBufferUTF8, + i); + i += Character.charCount(ch); + consume(ch); + } + } else { + for (int i = 0; i < rc; i++) { + consume(readBuffer[i]); } - - consume((char) ch); } } // Permit my enclosing UI to know that I updated. @@ -810,6 +826,31 @@ public class ECMA48 implements Runnable { // ECMA48 ----------------------------------------------------------------- // ------------------------------------------------------------------------ + /** + * Process keyboard and mouse events from the user. + * + * @param event the input event to consume + */ + private void handleUserEvent(final TInputEvent event) { + if (event instanceof TKeypressEvent) { + keypress(((TKeypressEvent) event).getKey()); + } + if (event instanceof TMouseEvent) { + mouse((TMouseEvent) event); + } + } + + /** + * Add a keyboard and mouse event from the user to the queue. + * + * @param event the input event to consume + */ + public void addUserEvent(final TInputEvent event) { + synchronized (userQueue) { + userQueue.add(event); + } + } + /** * Return the proper primary Device Attributes string. * @@ -830,10 +871,10 @@ public class ECMA48 implements Runnable { case XTERM: // "I am a VT220" - 7 bit version if (!s8c1t) { - return "\033[?62;1;6c"; + return "\033[?62;1;6;9;4c"; } // "I am a VT220" - 8 bit version - return "\u009b?62;1;6c"; + return "\u009b?62;1;6;9;4c"; default: throw new IllegalArgumentException("Invalid device type: " + type); } @@ -1051,7 +1092,7 @@ public class ECMA48 implements Runnable { * * @param width the new width */ - public final void setWidth(final int width) { + public final synchronized void setWidth(final int width) { this.width = width; rightMargin = width - 1; if (currentState.cursorX >= width) { @@ -1076,7 +1117,7 @@ public class ECMA48 implements Runnable { * * @param height the new height */ - public final void setHeight(final int height) { + public final synchronized void setHeight(final int height) { int delta = height - this.height; this.height = height; scrollRegionBottom += delta; @@ -1387,7 +1428,7 @@ public class ECMA48 implements Runnable { * * @param ch character to display */ - private void printCharacter(final char ch) { + private void printCharacter(final int ch) { int rightMargin = this.rightMargin; if (StringUtils.width(ch) == 2) { @@ -1495,7 +1536,7 @@ public class ECMA48 implements Runnable { * * @param mouse mouse event received from the local user */ - public void mouse(final TMouseEvent mouse) { + private void mouse(final TMouseEvent mouse) { /* System.err.printf("mouse(): protocol %s encoding %s mouse %s\n", @@ -1643,7 +1684,7 @@ public class ECMA48 implements Runnable { * * @param keypress keypress received from the local user */ - public void keypress(final TKeypress keypress) { + private void keypress(final TKeypress keypress) { writeRemote(keypressToString(keypress)); } @@ -1702,7 +1743,7 @@ public class ECMA48 implements Runnable { * the remote side. */ if (keypress.getChar() < 0x20) { - handleControlChar(keypress.getChar()); + handleControlChar((char) keypress.getChar()); } else { // Local echo for everything else printCharacter(keypress.getChar()); @@ -1717,17 +1758,17 @@ public class ECMA48 implements Runnable { // Handle control characters if ((keypress.isCtrl()) && (!keypress.isFnKey())) { StringBuilder sb = new StringBuilder(); - char ch = keypress.getChar(); + int ch = keypress.getChar(); ch -= 0x40; - sb.append(ch); + sb.append(Character.toChars(ch)); return sb.toString(); } // Handle alt characters if ((keypress.isAlt()) && (!keypress.isFnKey())) { StringBuilder sb = new StringBuilder("\033"); - char ch = keypress.getChar(); - sb.append(ch); + int ch = keypress.getChar(); + sb.append(Character.toChars(ch)); return sb.toString(); } @@ -2279,7 +2320,7 @@ public class ECMA48 implements Runnable { // Non-alt, non-ctrl characters if (!keypress.isFnKey()) { StringBuilder sb = new StringBuilder(); - sb.append(keypress.getChar()); + sb.append(Character.toChars(keypress.getChar())); return sb.toString(); } return ""; @@ -2294,7 +2335,7 @@ public class ECMA48 implements Runnable { * @param charsetGr character set defined for GR * @return character to display on the screen */ - private char mapCharacterCharset(final char ch, + private char mapCharacterCharset(final int ch, final CharacterSet charsetGl, final CharacterSet charsetGr) { @@ -2372,7 +2413,7 @@ public class ECMA48 implements Runnable { * @param ch either 8-bit or Unicode character from the remote side * @return character to display on the screen */ - private char mapCharacter(final char ch) { + private int mapCharacter(final int ch) { if (ch >= 0x100) { // Unicode character, just return it return ch; @@ -3419,8 +3460,7 @@ public class ECMA48 implements Runnable { * DECALN - Screen alignment display. */ private void decaln() { - Cell newCell = new Cell(); - newCell.setChar('E'); + Cell newCell = new Cell('E'); for (DisplayLine line: display) { for (int i = 0; i < line.length(); i++) { line.replace(i, newCell); @@ -4681,10 +4721,26 @@ public class ECMA48 implements Runnable { int i = getCsiParam(0, 0); if (!xtermPrivateModeFlag) { - if (i == 14) { - // Report xterm window in pixels as CSI 4 ; height ; width t + switch (i) { + case 14: + // Report xterm text area size in pixels as CSI 4 ; height ; + // width t writeRemote(String.format("\033[4;%d;%dt", textHeight * height, textWidth * width)); + break; + case 16: + // Report character size in pixels as CSI 6 ; height ; width + // t + writeRemote(String.format("\033[6;%d;%dt", textHeight, + textWidth)); + break; + case 18: + // Report the text are size in characters as CSI 8 ; height ; + // width t + writeRemote(String.format("\033[8;%d;%dt", height, width)); + break; + default: + break; } } } @@ -4694,14 +4750,14 @@ public class ECMA48 implements Runnable { * * @param ch character from the remote side */ - private void consume(char ch) { + private void consume(int ch) { // DEBUG // System.err.printf("%c STATE = %s\n", ch, scanState); // Special case for VT10x: 7-bit characters only if ((type == DeviceType.VT100) || (type == DeviceType.VT102)) { - ch = (char)(ch & 0x7F); + ch = (ch & 0x7F); } // Special "anywhere" states @@ -4771,7 +4827,7 @@ public class ECMA48 implements Runnable { // 00-17, 19, 1C-1F --> execute // 80-8F, 91-9A, 9C --> execute if ((ch <= 0x1F) || ((ch >= 0x80) && (ch <= 0x9F))) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-7F --> print @@ -4798,13 +4854,13 @@ public class ECMA48 implements Runnable { case ESCAPE: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); return; } // 20-2F --> collect, then switch to ESCAPE_INTERMEDIATE if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); scanState = ScanState.ESCAPE_INTERMEDIATE; return; } @@ -5140,12 +5196,12 @@ public class ECMA48 implements Runnable { case ESCAPE_INTERMEDIATE: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-2F --> collect if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); } // 30-7E --> dispatch, then switch to GROUND @@ -5747,12 +5803,12 @@ public class ECMA48 implements Runnable { case CSI_ENTRY: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-2F --> collect, then switch to CSI_INTERMEDIATE if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); scanState = ScanState.CSI_INTERMEDIATE; } @@ -5768,7 +5824,7 @@ public class ECMA48 implements Runnable { // 3C-3F --> collect, then switch to CSI_PARAM if ((ch >= 0x3C) && (ch <= 0x3F)) { - collect(ch); + collect((char) ch); scanState = ScanState.CSI_PARAM; } @@ -6020,12 +6076,12 @@ public class ECMA48 implements Runnable { case CSI_PARAM: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-2F --> collect, then switch to CSI_INTERMEDIATE if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); scanState = ScanState.CSI_INTERMEDIATE; } @@ -6274,12 +6330,12 @@ public class ECMA48 implements Runnable { case CSI_INTERMEDIATE: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-2F --> collect if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); } // 0x30-3F goes to CSI_IGNORE @@ -6387,12 +6443,12 @@ public class ECMA48 implements Runnable { case CSI_IGNORE: // 00-17, 19, 1C-1F --> execute if (ch <= 0x1F) { - handleControlChar(ch); + handleControlChar((char) ch); } // 20-2F --> collect if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); } // 40-7E --> ignore, then switch to GROUND @@ -6413,7 +6469,7 @@ public class ECMA48 implements Runnable { // 0x1B 0x5C goes to GROUND if (ch == 0x1B) { - collect(ch); + collect((char) ch); } if (ch == 0x5C) { if ((collectBuffer.length() > 0) @@ -6425,7 +6481,7 @@ public class ECMA48 implements Runnable { // 20-2F --> collect, then switch to DCS_INTERMEDIATE if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); scanState = ScanState.DCS_INTERMEDIATE; } @@ -6441,7 +6497,7 @@ public class ECMA48 implements Runnable { // 3C-3F --> collect, then switch to DCS_PARAM if ((ch >= 0x3C) && (ch <= 0x3F)) { - collect(ch); + collect((char) ch); scanState = ScanState.DCS_PARAM; } @@ -6471,7 +6527,7 @@ public class ECMA48 implements Runnable { // 0x1B 0x5C goes to GROUND if (ch == 0x1B) { - collect(ch); + collect((char) ch); } if (ch == 0x5C) { if ((collectBuffer.length() > 0) @@ -6503,7 +6559,7 @@ public class ECMA48 implements Runnable { // 0x1B 0x5C goes to GROUND if (ch == 0x1B) { - collect(ch); + collect((char) ch); } if (ch == 0x5C) { if ((collectBuffer.length() > 0) @@ -6515,7 +6571,7 @@ public class ECMA48 implements Runnable { // 20-2F --> collect, then switch to DCS_INTERMEDIATE if ((ch >= 0x20) && (ch <= 0x2F)) { - collect(ch); + collect((char) ch); scanState = ScanState.DCS_INTERMEDIATE; } @@ -6555,7 +6611,7 @@ public class ECMA48 implements Runnable { // 0x1B 0x5C goes to GROUND if (ch == 0x1B) { - collect(ch); + collect((char) ch); } if (ch == 0x5C) { if ((collectBuffer.length() > 0) @@ -6603,7 +6659,7 @@ public class ECMA48 implements Runnable { // 0x1B 0x5C goes to GROUND if (ch == 0x1B) { - collect(ch); + collect((char) ch); } if (ch == 0x5C) { if ((collectBuffer.length() > 0) @@ -6616,19 +6672,19 @@ public class ECMA48 implements Runnable { // 00-17, 19, 1C-1F, 20-7E --> put if (ch <= 0x17) { - sixelParseBuffer.append(ch); + sixelParseBuffer.append((char) ch); return; } if (ch == 0x19) { - sixelParseBuffer.append(ch); + sixelParseBuffer.append((char) ch); return; } if ((ch >= 0x1C) && (ch <= 0x1F)) { - sixelParseBuffer.append(ch); + sixelParseBuffer.append((char) ch); return; } if ((ch >= 0x20) && (ch <= 0x7E)) { - sixelParseBuffer.append(ch); + sixelParseBuffer.append((char) ch); return; } @@ -6641,11 +6697,11 @@ public class ECMA48 implements Runnable { // Special case for Jexer: PM can pass one control character if (ch == 0x1B) { - pmPut(ch); + pmPut((char) ch); } if ((ch >= 0x20) && (ch <= 0x7F)) { - pmPut(ch); + pmPut((char) ch); } // 0x9C goes to GROUND @@ -6658,14 +6714,14 @@ public class ECMA48 implements Runnable { case OSC_STRING: // Special case for Xterm: OSC can pass control characters if ((ch == 0x9C) || (ch == 0x07) || (ch == 0x1B)) { - oscPut(ch); + oscPut((char) ch); } // 00-17, 19, 1C-1F --> ignore // 20-7F --> osc_put if ((ch >= 0x20) && (ch <= 0x7F)) { - oscPut(ch); + oscPut((char) ch); } // 0x9C goes to GROUND @@ -6678,7 +6734,7 @@ public class ECMA48 implements Runnable { case VT52_DIRECT_CURSOR_ADDRESS: // This is a special case for the VT52 sequence "ESC Y l c" if (collectBuffer.length() == 0) { - collect(ch); + collect((char) ch); } else if (collectBuffer.length() == 1) { // We've got the two characters, one in the buffer and the // other in ch. @@ -6861,7 +6917,7 @@ public class ECMA48 implements Runnable { * @param ch the character to draw */ private void drawHalves(final int leftX, final int leftY, - final int rightX, final int rightY, final char ch) { + final int rightX, final int rightY, final int ch) { // System.err.println("drawHalves(): " + Integer.toHexString(ch)); @@ -6870,8 +6926,7 @@ public class ECMA48 implements Runnable { lastTextHeight = textHeight; } - Cell cell = new Cell(ch); - cell.setAttr(currentState.attr); + Cell cell = new Cell(ch, currentState.attr); BufferedImage image = glyphMaker.getImage(cell, textWidth * 2, textHeight); BufferedImage leftImage = image.getSubimage(0, 0, textWidth, @@ -6879,14 +6934,12 @@ public class ECMA48 implements Runnable { BufferedImage rightImage = image.getSubimage(textWidth, 0, textWidth, textHeight); - Cell left = new Cell(); - left.setTo(cell); + Cell left = new Cell(cell); left.setImage(leftImage); left.setWidth(Cell.Width.LEFT); display.get(leftY).replace(leftX, left); - Cell right = new Cell(); - right.setTo(cell); + Cell right = new Cell(cell); right.setImage(rightImage); right.setWidth(Cell.Width.RIGHT); display.get(rightY).replace(rightX, right);