LICENSE CHANGED TO MIT
[nikiroo-utils.git] / src / jexer / tterminal / ECMA48.java
index 017c3c7f1df6b6b347f1f862d9151de06231ce35..300625b9f244da6bb572f03607744abeea6ca81a 100644 (file)
@@ -1,29 +1,27 @@
-/**
+/*
  * Jexer - Java Text User Interface
  *
- * License: LGPLv3 or later
- *
- * This module is licensed under the GNU Lesser General Public License
- * Version 3.  Please see the file "COPYING" in this directory for more
- * information about the GNU Lesser General Public License Version 3.
+ * The MIT License (MIT)
  *
- *     Copyright (C) 2015  Kevin Lamonte
+ * Copyright (C) 2016 Kevin Lamonte
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * 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.
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see
- * http://www.gnu.org/licenses/, or write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
  *
  * @author Kevin Lamonte [kevin.lamonte@gmail.com]
  * @version 1
@@ -179,7 +177,7 @@ public class ECMA48 implements Runnable {
      * @param deviceType DeviceType.VT100, DeviceType, XTERM, etc.
      * @param baseLang a base language without UTF-8 flag such as "C" or
      * "en_US"
-     * @return "LANG=en_US", "LANG=en_US.UTF-8", etc.
+     * @return "en_US", "en_US.UTF-8", etc.
      */
     public static String deviceTypeLang(final DeviceType deviceType,
         final String baseLang) {
@@ -522,7 +520,8 @@ public class ECMA48 implements Runnable {
      */
     private enum MouseEncoding {
         X10,
-        UTF8
+        UTF8,
+        SGR
     }
 
     /**
@@ -614,7 +613,7 @@ public class ECMA48 implements Runnable {
      *
      * @return if true, the cursor is visible
      */
-    public final boolean visibleCursor() {
+    public final boolean isCursorVisible() {
         return cursorVisible;
     }
 
@@ -682,6 +681,7 @@ public class ECMA48 implements Runnable {
      * Whether number pad keys send VT100 or VT52, application or numeric
      * sequences.
      */
+    @SuppressWarnings("unused")
     private KeypadMode keypadMode;
 
     /**
@@ -690,6 +690,15 @@ public class ECMA48 implements Runnable {
     private boolean columns132 = false;
 
     /**
+     * Get 132 columns value.
+     *
+     * @return if true, the terminal is in 132 column mode
+     */
+    public final boolean isColumns132() {
+                return columns132;
+        }
+
+        /**
      * true = reverse video.  Set by DECSCNM.
      */
     private boolean reverseVideo = false;
@@ -891,7 +900,7 @@ public class ECMA48 implements Runnable {
      * @param type one of the DeviceType constants to select VT100, VT102,
      * VT220, or XTERM
      * @param inputStream an InputStream connected to the remote side.  For
-     * type == XTERM, inputStrem is converted to a Reader with UTF-8
+     * type == XTERM, inputStream is converted to a Reader with UTF-8
      * encoding.
      * @param outputStream an OutputStream connected to the remote user.  For
      * type == XTERM, outputStream is converted to a Writer with UTF-8
@@ -978,7 +987,6 @@ public class ECMA48 implements Runnable {
      * Handle a linefeed.
      */
     private void linefeed() {
-        int i;
 
         if (currentState.cursorY < scrollRegionBottom) {
             // Increment screen y
@@ -1102,7 +1110,7 @@ public class ECMA48 implements Runnable {
             mouseProtocol, mouseEncoding, mouse);
          */
 
-        if (mouseEncoding != MouseEncoding.UTF8) {
+        if (mouseEncoding == MouseEncoding.X10) {
             // We will support X10 but only for (160,94) and smaller.
             if ((mouse.getX() >= 160) || (mouse.getY() >= 94)) {
                 return;
@@ -1137,11 +1145,11 @@ public class ECMA48 implements Runnable {
              * have a button down (i.e. drag-and-drop).
              */
             if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
-                if (!mouse.getMouse1()
-                    && !mouse.getMouse2()
-                    && !mouse.getMouse3()
-                    && !mouse.getMouseWheelUp()
-                    && !mouse.getMouseWheelDown()
+                if (!mouse.isMouse1()
+                    && !mouse.isMouse2()
+                    && !mouse.isMouse3()
+                    && !mouse.isMouseWheelUp()
+                    && !mouse.isMouseWheelDown()
                 ) {
                     return;
                 }
@@ -1155,27 +1163,83 @@ public class ECMA48 implements Runnable {
 
         // Now encode the event
         StringBuilder sb = new StringBuilder(6);
-        sb.append((char) 0x1B);
-        sb.append('[');
-        sb.append('M');
-        if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
-            sb.append((char) (0x03 + 32));
-        } else if (mouse.getMouse1()) {
-            sb.append((char) (0x00 + 32));
-        } else if (mouse.getMouse2()) {
-            sb.append((char) (0x01 + 32));
-        } else if (mouse.getMouse3()) {
-            sb.append((char) (0x02 + 32));
-        } else if (mouse.getMouseWheelUp()) {
-            sb.append((char) (0x04 + 64));
-        } else if (mouse.getMouseWheelDown()) {
-            sb.append((char) (0x05 + 64));
+        if (mouseEncoding == MouseEncoding.SGR) {
+            sb.append((char) 0x1B);
+            sb.append("[<");
+
+            if (mouse.isMouse1()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append("32;");
+                } else {
+                    sb.append("0;");
+                }
+            } else if (mouse.isMouse2()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append("33;");
+                } else {
+                    sb.append("1;");
+                }
+            } else if (mouse.isMouse3()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append("34;");
+                } else {
+                    sb.append("2;");
+                }
+            } else if (mouse.isMouseWheelUp()) {
+                sb.append("64;");
+            } else if (mouse.isMouseWheelDown()) {
+                sb.append("65;");
+            } else {
+                // This is motion with no buttons down.
+                sb.append("35;");
+            }
+
+            sb.append(String.format("%d;%d", mouse.getX() + 1,
+                    mouse.getY() + 1));
+
+            if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+                sb.append("m");
+            } else {
+                sb.append("M");
+            }
+
         } else {
-            sb.append((char) (0x03 + 32));
-        }
+            // X10 and UTF8 encodings
+            sb.append((char) 0x1B);
+            sb.append('[');
+            sb.append('M');
+            if (mouse.getType() == TMouseEvent.Type.MOUSE_UP) {
+                sb.append((char) (0x03 + 32));
+            } else if (mouse.isMouse1()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append((char) (0x00 + 32 + 32));
+                } else {
+                    sb.append((char) (0x00 + 32));
+                }
+            } else if (mouse.isMouse2()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append((char) (0x01 + 32 + 32));
+                } else {
+                    sb.append((char) (0x01 + 32));
+                }
+            } else if (mouse.isMouse3()) {
+                if (mouse.getType() == TMouseEvent.Type.MOUSE_MOTION) {
+                    sb.append((char) (0x02 + 32 + 32));
+                } else {
+                    sb.append((char) (0x02 + 32));
+                }
+            } else if (mouse.isMouseWheelUp()) {
+                sb.append((char) (0x04 + 64));
+            } else if (mouse.isMouseWheelDown()) {
+                sb.append((char) (0x05 + 64));
+            } else {
+                // This is motion with no buttons down.
+                sb.append((char) (0x03 + 32));
+            }
 
-        sb.append((char) (mouse.getX() + 33));
-        sb.append((char) (mouse.getY() + 33));
+            sb.append((char) (mouse.getX() + 33));
+            sb.append((char) (mouse.getY() + 33));
+        }
 
         // System.err.printf("Would write: \'%s\'\n", sb.toString());
         writeRemote(sb.toString());
@@ -1199,16 +1263,16 @@ public class ECMA48 implements Runnable {
      */
     private String keypressToString(final TKeypress keypress) {
 
-        if ((fullDuplex == false) && (!keypress.getIsKey())) {
+        if ((fullDuplex == false) && (!keypress.isFnKey())) {
             /*
              * If this is a control character, process it like it came from
              * the remote side.
              */
-            if (keypress.getCh() < 0x20) {
-                handleControlChar(keypress.getCh());
+            if (keypress.getChar() < 0x20) {
+                handleControlChar(keypress.getChar());
             } else {
                 // Local echo for everything else
-                printCharacter(keypress.getCh());
+                printCharacter(keypress.getChar());
             }
         }
 
@@ -1218,18 +1282,18 @@ public class ECMA48 implements Runnable {
         }
 
         // Handle control characters
-        if ((keypress.getCtrl()) && (!keypress.getIsKey())) {
+        if ((keypress.isCtrl()) && (!keypress.isFnKey())) {
             StringBuilder sb = new StringBuilder();
-            char ch = keypress.getCh();
+            char ch = keypress.getChar();
             ch -= 0x40;
             sb.append(ch);
             return sb.toString();
         }
 
         // Handle alt characters
-        if ((keypress.getAlt()) && (!keypress.getIsKey())) {
+        if ((keypress.isAlt()) && (!keypress.isFnKey())) {
             StringBuilder sb = new StringBuilder("\033");
-            char ch = keypress.getCh();
+            char ch = keypress.getChar();
             sb.append(ch);
             return sb.toString();
         }
@@ -1627,9 +1691,9 @@ public class ECMA48 implements Runnable {
         }
 
         // Non-alt, non-ctrl characters
-        if (!keypress.getIsKey()) {
+        if (!keypress.isFnKey()) {
             StringBuilder sb = new StringBuilder();
-            sb.append(keypress.getCh());
+            sb.append(keypress.getChar());
             return sb.toString();
         }
         return "";
@@ -2119,8 +2183,9 @@ public class ECMA48 implements Runnable {
     }
 
     /**
-     * Set or unset a toggle.  value is 'true' for set ('h'), false for reset
-     * ('l').
+     * Set or unset a toggle.
+     *
+     * @param value true for set ('h'), false for reset ('l')
      */
     private void setToggle(final boolean value) {
         boolean decPrivateModeFlag = false;
@@ -2433,6 +2498,19 @@ public class ECMA48 implements Runnable {
                 }
                 break;
 
+            case 1006:
+                if ((type == DeviceType.XTERM)
+                    && (decPrivateModeFlag == true)
+                ) {
+                    // Mouse: SGR coordinates
+                    if (value == true) {
+                        mouseEncoding = MouseEncoding.SGR;
+                    } else {
+                        mouseEncoding = MouseEncoding.X10;
+                    }
+                }
+                break;
+
             default:
                 break;
 
@@ -3307,17 +3385,31 @@ public class ECMA48 implements Runnable {
      * DECSTBM - Set top and bottom margins.
      */
     private void decstbm() {
-        int top = getCsiParam(0, 1, 1, height) - 1;
-        int bottom = getCsiParam(1, height, 1, height) - 1;
+        boolean decPrivateModeFlag = false;
 
-        if (top > bottom) {
-            top = bottom;
+        for (int i = 0; i < collectBuffer.length(); i++) {
+            if (collectBuffer.charAt(i) == '?') {
+                decPrivateModeFlag = true;
+                break;
+            }
         }
-        scrollRegionTop = top;
-        scrollRegionBottom = bottom;
+        if (decPrivateModeFlag) {
+            // This could be restore DEC private mode values.
+            // Ignore it.
+        } else {
+            // DECSTBM
+            int top = getCsiParam(0, 1, 1, height) - 1;
+            int bottom = getCsiParam(1, height, 1, height) - 1;
 
-        // Home cursor
-        cursorPosition(0, 0);
+            if (top > bottom) {
+                top = bottom;
+            }
+            scrollRegionTop = top;
+            scrollRegionBottom = bottom;
+
+            // Home cursor
+            cursorPosition(0, 0);
+        }
     }
 
     /**
@@ -3524,7 +3616,7 @@ public class ECMA48 implements Runnable {
         for (int i = start; i <= end; i++) {
             DisplayLine line = display.get(currentState.cursorY);
             if ((!honorProtected)
-                || ((honorProtected) && (!line.charAt(i).getProtect()))) {
+                || ((honorProtected) && (!line.charAt(i).isProtect()))) {
 
                 switch (type) {
                 case VT100:
@@ -3683,7 +3775,7 @@ public class ECMA48 implements Runnable {
      *
      * @param ch character from the remote side
      */
-    public void consume(char ch) {
+    private void consume(char ch) {
 
         // DEBUG
         // System.err.printf("%c", ch);
@@ -5441,8 +5533,8 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.length() > 0) &&
-                    (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
+                if ((collectBuffer.length() > 0)
+                    && (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
                 ) {
                     toGround();
                 }
@@ -5473,8 +5565,8 @@ public class ECMA48 implements Runnable {
                 collect(ch);
             }
             if (ch == 0x5C) {
-                if ((collectBuffer.length() > 0) &&
-                    (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
+                if ((collectBuffer.length() > 0)
+                    && (collectBuffer.charAt(collectBuffer.length() - 1) == 0x1B)
                 ) {
                     toGround();
                 }
@@ -5626,7 +5718,7 @@ public class ECMA48 implements Runnable {
     /**
      * Read function runs on a separate thread.
      */
-    public void run() {
+    public final void run() {
         boolean utf8 = false;
         boolean done = false;