#35 support emoji in tterminal
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 12 Aug 2019 12:37:18 +0000 (07:37 -0500)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 12 Aug 2019 12:37:18 +0000 (07:37 -0500)
src/jexer/TEditorWidget.java
src/jexer/TField.java
src/jexer/TKeypress.java
src/jexer/backend/GlyphMaker.java
src/jexer/backend/SwingTerminal.java
src/jexer/bits/StringUtils.java
src/jexer/event/TKeypressEvent.java
src/jexer/tterminal/DisplayLine.java
src/jexer/tterminal/ECMA48.java

index f65ba6b5823b4d1592288488b3e0f1aba4c036b6..204b5750d3faa09d7c7c7a2f8b81460801195db8 100644 (file)
@@ -265,7 +265,8 @@ public class TEditorWidget extends TWidget {
             && !keypress.getKey().isCtrl()
         ) {
             // Plain old keystroke, process it
-            document.addChar(keypress.getKey().getChar());
+            // TODO: fix document to use ints, not chars
+            document.addChar((char) keypress.getKey().getChar());
             alignCursor();
         } else {
             // Pass other keys (tab etc.) on to TWidget
index 7b17ba87f7720da099233799792fed28a7572930..3d727ea17ce1d19003646ec6091570e6173bb860 100644 (file)
@@ -47,7 +47,7 @@ public class TField extends TWidget {
     /**
      * Background character for unfilled-in text.
      */
-    protected char backgroundChar = GraphicsChars.HATCH;
+    protected int backgroundChar = GraphicsChars.HATCH;
 
     /**
      * Field text.
@@ -412,7 +412,7 @@ public class TField extends TWidget {
      *
      * @return background character
      */
-    public final char getBackgroundChar() {
+    public final int getBackgroundChar() {
         return backgroundChar;
     }
 
@@ -421,7 +421,7 @@ public class TField extends TWidget {
      *
      * @param backgroundChar the background character
      */
-    public void setBackgroundChar(final char backgroundChar) {
+    public void setBackgroundChar(final int backgroundChar) {
         this.backgroundChar = backgroundChar;
     }
 
@@ -521,9 +521,9 @@ public class TField extends TWidget {
     /**
      * Append char to the end of the field.
      *
-     * @param ch char to append
+     * @param ch char to append
      */
-    protected void appendChar(final char ch) {
+    protected void appendChar(final int ch) {
         // Append the LAST character
         text += ch;
         position++;
@@ -548,8 +548,8 @@ public class TField extends TWidget {
      *
      * @param ch char to append
      */
-    protected void insertChar(final char ch) {
-        text = text.substring(0, position) + ch + text.substring(position);
+    protected void insertChar(final int ch) {
+        text = text.substring(0, position) + ((char) ch) + text.substring(position);
         position++;
         screenPosition += StringUtils.width(ch);
         if ((screenPosition - windowStart) == getWidth()) {
index 7713e552529f49e0010bc81a627b523e75d097b1..c965e7dbab48873ae31a35963d4aaef4231cdcaf 100644 (file)
@@ -622,7 +622,7 @@ public class TKeypress {
      * Backspace as ^?.
      */
     public static final TKeypress kbBackspaceDel = new TKeypress(false,
-            0, (char)0x7F, false, false, false);
+            0, (char) 0x7F, false, false, false);
 
     // ------------------------------------------------------------------------
     // Variables --------------------------------------------------------------
@@ -656,7 +656,7 @@ public class TKeypress {
     /**
      * The character received.
      */
-    private char ch;
+    private int ch;
 
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
@@ -672,7 +672,7 @@ public class TKeypress {
      * @param ctrl if true, CTRL was pressed with this keystroke
      * @param shift if true, SHIFT was pressed with this keystroke
      */
-    public TKeypress(final boolean isKey, final int fnKey, final char ch,
+    public TKeypress(final boolean isKey, final int fnKey, final int ch,
             final boolean alt, final boolean ctrl, final boolean shift) {
 
         this.isFunctionKey = isKey;
@@ -737,7 +737,7 @@ public class TKeypress {
      *
      * @return the character (only valid if isKey is false)
      */
-    public char getChar() {
+    public int getChar() {
         return ch;
     }
 
index 2a6610bbec4c8cedff479a6962b8502757bac874..ef7696974eb32f599391cb17597c0b2a71af6226 100644 (file)
@@ -413,7 +413,8 @@ public class GlyphMaker {
         if ((ch >= 0x2e80) && (ch <= 0x9fff)) {
             return makerCjk.getImage(cell, cellWidth, cellHeight, blinkVisible);
         }
-        if ((ch >= 0x1f004) && (ch <= 0x1f9c0)) {
+        if ((ch >= 0x1f004) && (ch <= 0x1fffd)) {
+            // System.err.println("emoji: " + String.format("0x%x", ch));
             return makerEmoji.getImage(cell, cellWidth, cellHeight, blinkVisible);
         }
 
index a3825b852548c86bccf45817df32756da5ea729b..65decfa3642688047465d568dfff2a6ccd88126c 100644 (file)
@@ -1828,6 +1828,7 @@ public class SwingTerminal extends LogicalScreen
                 break;
             default:
                 if (!alt && ctrl && !shift) {
+                    // Control character, replace ch with 'A', 'B', etc.
                     ch = KeyEvent.getKeyText(key.getKeyCode()).charAt(0);
                 }
                 // Not a special key, put it together
index 26d90a400577bf663f0b4550d1429de628c68b01..12a13908bb16cf10c0b13f291eb0c048eec8b4af 100644 (file)
@@ -450,7 +450,7 @@ public class StringUtils {
                 || ((ch >= 0x20000) && (ch <= 0x2fffd))
                 || ((ch >= 0x30000) && (ch <= 0x3fffd))
                 // emoji
-                || ((ch >= 0x1f004) && (ch <= 0x1f9c0))
+                || ((ch >= 0x1f004) && (ch <= 0x1fffd))
             )
         ) {
             return 2;
index b56f08baa580fb022a0f91f644b51316a7539325..79b28f29381655022f879ef0eccd0f472d50307d 100644 (file)
@@ -67,7 +67,7 @@ public class TKeypressEvent extends TInputEvent {
      * @param ctrl if true, CTRL was pressed with this keystroke
      * @param shift if true, SHIFT was pressed with this keystroke
      */
-    public TKeypressEvent(final boolean isKey, final int fnKey, final char ch,
+    public TKeypressEvent(final boolean isKey, final int fnKey, final int ch,
         final boolean alt, final boolean ctrl, final boolean shift) {
 
         this.key = new TKeypress(isKey, fnKey, ch, alt, ctrl, shift);
index a32da9b4d603b7dd96d26807f6745c89d2549624..06a05a330ddccb50cc3ecf142cea0c4c87c38df9 100644 (file)
@@ -221,7 +221,7 @@ public class DisplayLine {
      * @param idx the character index
      * @param ch the new char
      */
-    public void setChar(final int idx, final char ch) {
+    public void setChar(final int idx, final int ch) {
         chars[idx].setChar(ch);
     }
 
index 7ce95d6255e34bce8081bf2d54a23b6e92faddce..9ff37a181fdfc0871e086d47782a1c94f7852904 100644 (file)
@@ -350,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
@@ -750,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.
@@ -1426,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) {
@@ -1741,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());
@@ -1756,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();
         }
 
@@ -2318,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 "";
@@ -2333,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) {
 
@@ -2411,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;
@@ -4732,14 +4734,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
@@ -4809,7 +4811,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
@@ -4836,13 +4838,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;
             }
@@ -5178,12 +5180,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
@@ -5785,12 +5787,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;
             }
 
@@ -5806,7 +5808,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;
             }
 
@@ -6058,12 +6060,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;
             }
 
@@ -6312,12 +6314,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
@@ -6425,12 +6427,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
@@ -6451,7 +6453,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)
@@ -6463,7 +6465,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;
             }
 
@@ -6479,7 +6481,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;
             }
 
@@ -6509,7 +6511,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)
@@ -6541,7 +6543,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)
@@ -6553,7 +6555,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;
             }
 
@@ -6593,7 +6595,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)
@@ -6641,7 +6643,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)
@@ -6679,11 +6681,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
@@ -6696,14 +6698,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
@@ -6716,7 +6718,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.
@@ -6899,7 +6901,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));