X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Ftterminal%2FECMA48.java;h=eb13c0b4d35e43176e86568a65e6ef936042b58f;hb=d610cbd252f5595f2558960b97424d67c7ffe84c;hp=9376828ee7689411ff7492de501d7109bc892445;hpb=051e29138b18fb4b731a72f8727475b10e4c74e4;p=fanfix.git diff --git a/src/jexer/tterminal/ECMA48.java b/src/jexer/tterminal/ECMA48.java index 9376828..eb13c0b 100644 --- a/src/jexer/tterminal/ECMA48.java +++ b/src/jexer/tterminal/ECMA48.java @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (C) 2017 Kevin Lamonte + * Copyright (C) 2019 Kevin Lamonte * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,17 +29,18 @@ package jexer.tterminal; import java.io.BufferedOutputStream; +import java.io.CharArrayWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import jexer.TKeypress; @@ -252,12 +253,17 @@ public class ECMA48 implements Runnable { /** * The scrollback buffer characters + attributes. */ - private volatile List scrollback; + private volatile ArrayList scrollback; /** * The raw display buffer characters + attributes. */ - private volatile List display; + private volatile ArrayList display; + + /** + * The maximum number of lines in the scrollback buffer. + */ + private int maxScrollback = 10000; /** * The terminal's input. For type == XTERM, this is an InputStreamReader @@ -296,6 +302,15 @@ public class ECMA48 implements Runnable { */ private MouseEncoding mouseEncoding = MouseEncoding.X10; + /** + * A terminal may request that the mouse pointer be hidden using a + * Privacy Message containing either "hideMousePointer" or + * "showMousePointer". This is currently only used within Jexer by + * TTerminalWindow so that only the bottom-most instance of nested + * Jexer's draws the mouse within its application window. + */ + private boolean hideMousePointer = false; + /** * Physical display width. We start at 80x24, but the user can resize us * bigger/smaller. @@ -585,8 +600,8 @@ public class ECMA48 implements Runnable { csiParams = new ArrayList(); tabStops = new ArrayList(); - scrollback = new LinkedList(); - display = new LinkedList(); + scrollback = new ArrayList(); + display = new ArrayList(); this.type = type; if (inputStream instanceof TimeoutInputStream) { @@ -697,7 +712,7 @@ public class ECMA48 implements Runnable { ch = readBuffer[i]; } - consume((char)ch); + consume((char) ch); } } // Permit my enclosing UI to know that I updated. @@ -707,8 +722,29 @@ public class ECMA48 implements Runnable { } // System.err.println("end while loop"); System.err.flush(); } catch (IOException e) { - e.printStackTrace(); done = true; + + // This is an unusual case. We want to see the stack trace, + // but it is related to the spawned process rather than the + // actual UI. We will generate the stack trace, and consume + // it as though it was emitted by the shell. + CharArrayWriter writer= new CharArrayWriter(); + // Send a ST and RIS to clear the emulator state. + try { + writer.write("\033\\\033c"); + writer.write("\n-----------------------------------\n"); + e.printStackTrace(new PrintWriter(writer)); + writer.write("\n-----------------------------------\n"); + } catch (IOException e2) { + // SQUASH + } + char [] stackTrace = writer.toCharArray(); + for (int i = 0; i < stackTrace.length; i++) { + if (stackTrace[i] == '\n') { + consume('\r'); + } + consume(stackTrace[i]); + } } } // while ((done == false) && (stopReaderThread == false)) @@ -889,7 +925,7 @@ public class ECMA48 implements Runnable { try { readerThread.join(1000); } catch (InterruptedException e) { - e.printStackTrace(); + // SQUASH } } @@ -1078,7 +1114,7 @@ public class ECMA48 implements Runnable { private void resetTabStops() { tabStops.clear(); for (int i = 0; (i * 8) <= rightMargin; i++) { - tabStops.add(new Integer(i * 8)); + tabStops.add(Integer.valueOf(i * 8)); } } @@ -1234,7 +1270,12 @@ public class ECMA48 implements Runnable { private void newDisplayLine() { // Scroll the top line off into the scrollback buffer scrollback.add(display.get(0)); + if (scrollback.size() > maxScrollback) { + scrollback.remove(0); + scrollback.trimToSize(); + } display.remove(0); + display.trimToSize(); DisplayLine line = new DisplayLine(currentState.attr); line.setReverseColor(reverseVideo); display.add(line); @@ -1588,6 +1629,7 @@ public class ECMA48 implements Runnable { * @param keypress keypress received from the local user * @return string to transmit to the remote side */ + @SuppressWarnings("fallthrough") private String keypressToString(final TKeypress keypress) { if ((fullDuplex == false) && (!keypress.isFnKey())) { @@ -2326,13 +2368,13 @@ public class ECMA48 implements Runnable { switch (currentState.glLockshift) { case G1_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G2_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G3_GR: - assert (false); + throw new IllegalArgumentException("programming bug"); case G2_GL: // LS2 @@ -2353,10 +2395,10 @@ public class ECMA48 implements Runnable { switch (currentState.grLockshift) { case G2_GL: - assert (false); + throw new IllegalArgumentException("programming bug"); case G3_GL: - assert (false); + throw new IllegalArgumentException("programming bug"); case G1_GR: // LS1R @@ -2415,7 +2457,7 @@ public class ECMA48 implements Runnable { display.size()); List displayMiddle = display.subList(regionBottom + 1 - remaining, regionBottom + 1); - display = new LinkedList(displayTop); + display = new ArrayList(displayTop); display.addAll(displayMiddle); for (int i = 0; i < n; i++) { DisplayLine line = new DisplayLine(currentState.attr); @@ -2456,7 +2498,7 @@ public class ECMA48 implements Runnable { display.size()); List displayMiddle = display.subList(regionTop, regionTop + remaining); - display = new LinkedList(displayTop); + display = new ArrayList(displayTop); for (int i = 0; i < n; i++) { DisplayLine line = new DisplayLine(currentState.attr); line.setReverseColor(reverseVideo); @@ -2611,7 +2653,7 @@ public class ECMA48 implements Runnable { */ private void param(final byte ch) { if (csiParams.size() == 0) { - csiParams.add(new Integer(0)); + csiParams.add(Integer.valueOf(0)); } Integer x = csiParams.get(csiParams.size() - 1); if ((ch >= '0') && (ch <= '9')) { @@ -2620,8 +2662,8 @@ public class ECMA48 implements Runnable { csiParams.set(csiParams.size() - 1, x); } - if (ch == ';') { - csiParams.add(new Integer(0)); + if ((ch == ';') && (csiParams.size() < 16)) { + csiParams.add(Integer.valueOf(0)); } } @@ -4053,12 +4095,12 @@ public class ECMA48 implements Runnable { if (collectBuffer.charAt(0) == '>') { extendedFlag = 1; if (collectBuffer.length() >= 2) { - i = Integer.parseInt(args.toString()); + i = Integer.parseInt(args); } } else if (collectBuffer.charAt(0) == '=') { extendedFlag = 2; if (collectBuffer.length() >= 2) { - i = Integer.parseInt(args.toString()); + i = Integer.parseInt(args); } } else { // Unknown code, bail out @@ -4500,7 +4542,7 @@ public class ECMA48 implements Runnable { args = collectBuffer.substring(0, collectBuffer.length() - 2); } - String [] p = args.toString().split(";"); + String [] p = args.split(";"); if (p.length > 0) { if ((p[0].equals("0")) || (p[0].equals("2"))) { if (p.length > 1) { @@ -4527,6 +4569,38 @@ public class ECMA48 implements Runnable { } } + /** + * Handle the SCAN_SOSPMAPC_STRING state. This is currently only used by + * Jexer ECMA48Terminal to talk to ECMA48. + * + * @param pmChar the character received from the remote side + */ + private void pmPut(final char pmChar) { + // System.err.println("pmPut: " + pmChar); + + // Collect first + collectBuffer.append(pmChar); + + // Xterm cases... + if (collectBuffer.toString().endsWith("\033\\")) { + String arg = null; + arg = collectBuffer.substring(0, collectBuffer.length() - 2); + + // System.err.println("arg: '" + arg + "'"); + + if (arg.equals("hideMousePointer")) { + hideMousePointer = true; + } + if (arg.equals("showMousePointer")) { + hideMousePointer = false; + } + + // Go to SCAN_GROUND state + toGround(); + return; + } + } + /** * Run this input character through the ECMA48 state machine. * @@ -4556,9 +4630,11 @@ public class ECMA48 implements Runnable { // 0x1B == ESCAPE if (ch == 0x1B) { if ((type == DeviceType.XTERM) - && (scanState == ScanState.OSC_STRING) + && ((scanState == ScanState.OSC_STRING) + || (scanState == ScanState.SOSPMAPC_STRING)) ) { // Xterm can pass ESCAPE to its OSC sequence. + // Jexer can pass ESCAPE to its PM sequence. } else if ((scanState != ScanState.DCS_ENTRY) && (scanState != ScanState.DCS_INTERMEDIATE) && (scanState != ScanState.DCS_IGNORE) @@ -6414,6 +6490,15 @@ public class ECMA48 implements Runnable { case SOSPMAPC_STRING: // 00-17, 19, 1C-1F, 20-7F --> ignore + // Special case for Jexer: PM can pass one control character + if (ch == 0x1B) { + pmPut(ch); + } + + if ((ch >= 0x20) && (ch <= 0x7F)) { + pmPut(ch); + } + // 0x9C goes to GROUND if (ch == 0x9C) { toGround(); @@ -6477,4 +6562,15 @@ public class ECMA48 implements Runnable { return currentState.cursorY; } + /** + * Returns true if this terminal has requested the mouse pointer be + * hidden. + * + * @return true if this terminal has requested the mouse pointer be + * hidden + */ + public final boolean hasHiddenMousePointer() { + return hideMousePointer; + } + }