--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.le/share/classes/jdk/internal/org/jline/terminal/impl/PosixPtyTerminal.java Tue Dec 11 11:29:28 2018 +0100
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2002-2018, the original author or authors.
+ *
+ * This software is distributable under the BSD license. See the terms of the
+ * BSD license in the documentation provided with this software.
+ *
+ * http://www.opensource.org/licenses/bsd-license.php
+ */
+package jdk.internal.org.jline.terminal.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jdk.internal.org.jline.terminal.spi.Pty;
+import jdk.internal.org.jline.utils.ClosedException;
+import jdk.internal.org.jline.utils.NonBlocking;
+import jdk.internal.org.jline.utils.NonBlockingInputStream;
+import jdk.internal.org.jline.utils.NonBlockingReader;
+
+public class PosixPtyTerminal extends AbstractPosixTerminal {
+
+ private final InputStream in;
+ private final OutputStream out;
+ private final InputStream masterInput;
+ private final OutputStream masterOutput;
+ private final NonBlockingInputStream input;
+ private final OutputStream output;
+ private final NonBlockingReader reader;
+ private final PrintWriter writer;
+
+ private final Object lock = new Object();
+ private Thread inputPumpThread;
+ private Thread outputPumpThread;
+ private boolean paused = true;
+
+ public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding) throws IOException {
+ this(name, type, pty, in, out, encoding, SignalHandler.SIG_DFL);
+ }
+
+ public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler) throws IOException {
+ this(name, type, pty, in, out, encoding, signalHandler, false);
+ }
+
+ public PosixPtyTerminal(String name, String type, Pty pty, InputStream in, OutputStream out, Charset encoding, SignalHandler signalHandler, boolean paused) throws IOException {
+ super(name, type, pty, encoding, signalHandler);
+ this.in = Objects.requireNonNull(in);
+ this.out = Objects.requireNonNull(out);
+ this.masterInput = pty.getMasterInput();
+ this.masterOutput = pty.getMasterOutput();
+ this.input = new InputStreamWrapper(NonBlocking.nonBlocking(name, pty.getSlaveInput()));
+ this.output = pty.getSlaveOutput();
+ this.reader = NonBlocking.nonBlocking(name, input, encoding());
+ this.writer = new PrintWriter(new OutputStreamWriter(output, encoding()));
+ parseInfoCmp();
+ if (!paused) {
+ resume();
+ }
+ }
+
+ public InputStream input() {
+ return input;
+ }
+
+ public NonBlockingReader reader() {
+ return reader;
+ }
+
+ public OutputStream output() {
+ return output;
+ }
+
+ public PrintWriter writer() {
+ return writer;
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ reader.close();
+ }
+
+ @Override
+ public boolean canPauseResume() {
+ return true;
+ }
+
+ @Override
+ public void pause() {
+ synchronized (lock) {
+ paused = true;
+ }
+ }
+
+ @Override
+ public void pause(boolean wait) throws InterruptedException {
+ Thread p1, p2;
+ synchronized (lock) {
+ paused = true;
+ p1 = inputPumpThread;
+ p2 = outputPumpThread;
+ }
+ if (p1 != null) {
+ p1.interrupt();
+ }
+ if (p2 != null) {
+ p2.interrupt();
+ }
+ if (p1 != null) {
+ p1.join();
+ }
+ if (p2 !=null) {
+ p2.join();
+ }
+ }
+
+ @Override
+ public void resume() {
+ synchronized (lock) {
+ paused = false;
+ if (inputPumpThread == null) {
+ inputPumpThread = new Thread(this::pumpIn, toString() + " input pump thread");
+ inputPumpThread.setDaemon(true);
+ inputPumpThread.start();
+ }
+ if (outputPumpThread == null) {
+ outputPumpThread = new Thread(this::pumpOut, toString() + " output pump thread");
+ outputPumpThread.setDaemon(true);
+ outputPumpThread.start();
+ }
+ }
+ }
+
+ @Override
+ public boolean paused() {
+ synchronized (lock) {
+ return paused;
+ }
+ }
+
+ private class InputStreamWrapper extends NonBlockingInputStream {
+
+ private final NonBlockingInputStream in;
+ private final AtomicBoolean closed = new AtomicBoolean();
+
+ protected InputStreamWrapper(NonBlockingInputStream in) {
+ this.in = in;
+ }
+
+ @Override
+ public int read(long timeout, boolean isPeek) throws IOException {
+ if (closed.get()) {
+ throw new ClosedException();
+ }
+ return in.read(timeout, isPeek);
+ }
+
+ @Override
+ public void close() throws IOException {
+ closed.set(true);
+ }
+ }
+
+ private void pumpIn() {
+ try {
+ for (;;) {
+ synchronized (lock) {
+ if (paused) {
+ inputPumpThread = null;
+ return;
+ }
+ }
+ int b = in.read();
+ if (b < 0) {
+ input.close();
+ break;
+ }
+ masterOutput.write(b);
+ masterOutput.flush();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ synchronized (lock) {
+ inputPumpThread = null;
+ }
+ }
+ }
+
+ private void pumpOut() {
+ try {
+ for (;;) {
+ synchronized (lock) {
+ if (paused) {
+ outputPumpThread = null;
+ return;
+ }
+ }
+ int b = masterInput.read();
+ if (b < 0) {
+ input.close();
+ break;
+ }
+ out.write(b);
+ out.flush();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ synchronized (lock) {
+ outputPumpThread = null;
+ }
+ }
+ try {
+ close();
+ } catch (Throwable t) {
+ // Ignore
+ }
+ }
+
+}