Skip to content

Commit

Permalink
Fix char encoding when writing directly to Windows console
Browse files Browse the repository at this point in the history
  • Loading branch information
jfdenise committed Jun 13, 2018
1 parent 912cecf commit 124d306
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@
import java.io.PipedOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.aesh.io.Encoder;
import org.aesh.readline.util.LoggerUtil;
import org.aesh.terminal.tty.Signal;
import org.aesh.terminal.tty.Size;
import static org.fusesource.jansi.internal.Kernel32.GetStdHandle;
Expand All @@ -48,16 +53,18 @@

abstract class AbstractWindowsTerminal extends AbstractTerminal {

private static class ConsoleOutput extends OutputStream {
private static class ConsoleOutput implements Consumer<int[]> {

private static final Logger LOGGER = LoggerUtil.getLogger(AbstractWindowsTerminal.class.getName());
private static final long console = GetStdHandle(STD_OUTPUT_HANDLE);
private final int[] writtenChars = new int[1];

@Override
public void write(int b) throws IOException {
char[] chars = new char[]{(char) b};
if (WriteConsoleW(console, chars, 1, writtenChars, 0) == 0) {
throw new IOException("Failed to write to console: " + WindowsSupport.getLastErrorMessage());
public void accept(int[] input) {
CharBuffer buffer = Encoder.toCharBuffer(input);
char[] chars = buffer.array();
if (WriteConsoleW(console, chars, chars.length, writtenChars, 0) == 0) {
LOGGER.log(Level.WARNING, "Failed to write out.", WindowsSupport.getLastErrorMessage());
}
}

Expand All @@ -82,17 +89,15 @@ public void write(int b) throws IOException {
protected final Thread pump;

private volatile boolean closing;
private final ConsoleOutput cpConsumer;

public AbstractWindowsTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException {
this(null, name, nativeSignals, signalHandler);
}

public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException {
public AbstractWindowsTerminal(boolean consumeCP, OutputStream output, String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException {
super(name, "windows", signalHandler);
PipedInputStream input = new PipedInputStream(PIPE_SIZE);
this.slaveInputPipe = new PipedOutputStream(input);
this.input = new FilterInputStream(input) {};
this.output = output == null ? new ConsoleOutput() : output;
this.cpConsumer = consumeCP ? new ConsoleOutput() : null;
this.output = output;
String encoding = getConsoleEncoding();
if (encoding == null) {
encoding = Charset.defaultCharset().name();
Expand All @@ -116,6 +121,11 @@ public AbstractWindowsTerminal(OutputStream output, String name, boolean nativeS
ShutdownHooks.add(closer);
}

@Override
public Consumer<int[]> getCodePointConsumer() {
return cpConsumer;
}

@Override
protected void handleDefaultSignal(Signal signal) {
Object handler = nativeHandlers.get(signal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public WinSysTerminal(String name, boolean nativeSignals) throws IOException {
}

public WinSysTerminal(String name, boolean nativeSignals, SignalHandler signalHandler) throws IOException {
super(setVTMode() ? null : new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out)), name, nativeSignals, signalHandler);
super(setVTMode(), new WindowsAnsiOutputStream(new FileOutputStream(FileDescriptor.out)), name, nativeSignals, signalHandler);
}

protected int getConsoleOutputCP() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class TerminalConnection implements Connection {

private Consumer<Size> sizeHandler;
private Decoder decoder;
private Encoder stdOut;
private Consumer<int[]> stdOut;
private Attributes attributes;
private EventDecoder eventDecoder;
private volatile boolean reading = false;
Expand Down Expand Up @@ -140,8 +140,12 @@ private void init(Terminal term) {

eventDecoder = new EventDecoder(attributes);
decoder = new Decoder(512, inputEncoding(), eventDecoder);
stdOut = new Encoder(outputEncoding(), this::write);

if(terminal.getCodePointConsumer() == null) {
stdOut = new Encoder(outputEncoding(), this::write);
} else {
stdOut = terminal.getCodePointConsumer();
}
if(terminal instanceof ExternalTerminal)
ansi = false;

Expand Down
11 changes: 7 additions & 4 deletions terminal-api/src/main/java/org/aesh/io/Encoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ public void setCharset(Charset charset) {

@Override
public void accept(int[] input) {
final char[] tmp = new char[2];
ByteBuffer bytesBuf = charset.encode(toCharBuffer(input));
out.accept(safeTrim(bytesBuf.array(), bytesBuf.limit()));
}

public static CharBuffer toCharBuffer(int[] input) {
final char[] tmp = new char[2];
int capacity = 0;
for (int codePoint : input) {
capacity += Character.charCount(codePoint);
Expand All @@ -59,9 +64,7 @@ public void accept(int[] input) {
charBuf.put(tmp, 0, size);
}
charBuf.flip();
ByteBuffer bytesBuf = charset.encode(charBuf);

out.accept(safeTrim(bytesBuf.array(), bytesBuf.limit()));
return charBuf;
}

private static byte[] safeTrim(byte[] bytes, int length) {
Expand Down
4 changes: 4 additions & 0 deletions terminal-api/src/main/java/org/aesh/terminal/Terminal.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.function.Consumer;
import org.aesh.terminal.tty.Signal;
import org.aesh.terminal.tty.Size;

Expand Down Expand Up @@ -57,4 +58,7 @@ interface SignalHandler {
// Infocmp capabilities
Device device();

default Consumer<int[]> getCodePointConsumer() {
return null;
}
}

0 comments on commit 124d306

Please sign in to comment.