blob: 2814b52c7ccd382eb1995ba8510454a5be110a4f [file] [log] [blame]
/*
* 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.
*
* https://opensource.org/licenses/BSD-3-Clause
*/
package jdk.internal.org.jline.terminal.impl;
import jdk.internal.org.jline.terminal.Attributes;
import jdk.internal.org.jline.terminal.Cursor;
import jdk.internal.org.jline.terminal.Size;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.IntConsumer;
/**
* Console implementation with embedded line disciplined.
*
* This terminal is well-suited for supporting incoming external
* connections, such as from the network (through telnet, ssh,
* or any kind of protocol).
* The terminal will start consuming the input in a separate thread
* to generate interruption events.
*
* @see LineDisciplineTerminal
*/
public class ExternalTerminal extends LineDisciplineTerminal {
protected final AtomicBoolean closed = new AtomicBoolean();
protected final InputStream masterInput;
protected final Object lock = new Object();
protected boolean paused = true;
protected Thread pumpThread;
public ExternalTerminal(String name, String type,
InputStream masterInput,
OutputStream masterOutput,
Charset encoding) throws IOException {
this(name, type, masterInput, masterOutput, encoding, SignalHandler.SIG_DFL);
}
public ExternalTerminal(String name, String type,
InputStream masterInput,
OutputStream masterOutput,
Charset encoding,
SignalHandler signalHandler) throws IOException {
this(name, type, masterInput, masterOutput, encoding, signalHandler, false);
}
public ExternalTerminal(String name, String type,
InputStream masterInput,
OutputStream masterOutput,
Charset encoding,
SignalHandler signalHandler,
boolean paused) throws IOException {
this(name, type, masterInput, masterOutput, encoding, signalHandler, paused, null, null);
}
public ExternalTerminal(String name, String type,
InputStream masterInput,
OutputStream masterOutput,
Charset encoding,
SignalHandler signalHandler,
boolean paused,
Attributes attributes,
Size size) throws IOException {
super(name, type, masterOutput, encoding, signalHandler);
this.masterInput = masterInput;
if (attributes != null) {
setAttributes(attributes);
}
if (size != null) {
setSize(size);
}
if (!paused) {
resume();
}
}
protected void doClose() throws IOException {
if (closed.compareAndSet(false, true)) {
pause();
super.doClose();
}
}
@Override
public boolean canPauseResume() {
return true;
}
@Override
public void pause() {
synchronized (lock) {
paused = true;
}
}
@Override
public void pause(boolean wait) throws InterruptedException {
Thread p;
synchronized (lock) {
paused = true;
p = pumpThread;
}
if (p != null) {
p.interrupt();
p.join();
}
}
@Override
public void resume() {
synchronized (lock) {
paused = false;
if (pumpThread == null) {
pumpThread = new Thread(this::pump, toString() + " input pump thread");
pumpThread.setDaemon(true);
pumpThread.start();
}
}
}
@Override
public boolean paused() {
synchronized (lock) {
return paused;
}
}
public void pump() {
try {
byte[] buf = new byte[1024];
while (true) {
int c = masterInput.read(buf);
if (c >= 0) {
processInputBytes(buf, 0, c);
}
if (c < 0 || closed.get()) {
break;
}
synchronized (lock) {
if (paused) {
pumpThread = null;
return;
}
}
}
} catch (IOException e) {
processIOException(e);
} finally {
synchronized (lock) {
pumpThread = null;
}
}
try {
slaveInput.close();
} catch (IOException e) {
// ignore
}
}
@Override
public Cursor getCursorPosition(IntConsumer discarded) {
return CursorSupport.getCursorPosition(this, discarded);
}
}