Skip to content

Commit

Permalink
Fix FFM backend on Windows (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
Glavo authored Sep 29, 2023
1 parent 3b15c26 commit 27a7bb5
Show file tree
Hide file tree
Showing 7 changed files with 362 additions and 260 deletions.
5 changes: 2 additions & 3 deletions src/main/java/org/fusesource/jansi/AnsiConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Locale;

import org.fusesource.jansi.internal.OSInfo;
import org.fusesource.jansi.io.AnsiOutputStream;
import org.fusesource.jansi.io.AnsiProcessor;
import org.fusesource.jansi.io.FastBufferedOutputStream;
Expand Down Expand Up @@ -194,8 +194,7 @@ public static int getTerminalWidth() {
return w;
}

static final boolean IS_WINDOWS =
System.getProperty("os.name").toLowerCase(Locale.ENGLISH).contains("win");
static final boolean IS_WINDOWS = OSInfo.isWindows();

static final boolean IS_CYGWIN =
IS_WINDOWS && System.getenv("PWD") != null && System.getenv("PWD").startsWith("/");
Expand Down
94 changes: 14 additions & 80 deletions src/main/java/org/fusesource/jansi/ffm/AnsiConsoleSupportFfm.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,93 +18,28 @@
import java.io.IOException;
import java.io.OutputStream;
import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.Linker;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.VarHandle;

import org.fusesource.jansi.AnsiConsoleSupport;
import org.fusesource.jansi.internal.OSInfo;
import org.fusesource.jansi.io.AnsiProcessor;

import static org.fusesource.jansi.ffm.Kernel32.*;

public class AnsiConsoleSupportFfm implements AnsiConsoleSupport {
static GroupLayout wsLayout;
static MethodHandle ioctl;
static VarHandle ws_col;
static MethodHandle isatty;

static {
wsLayout = MemoryLayout.structLayout(
ValueLayout.JAVA_SHORT.withName("ws_row"),
ValueLayout.JAVA_SHORT.withName("ws_col"),
ValueLayout.JAVA_SHORT,
ValueLayout.JAVA_SHORT);
ws_col = wsLayout.varHandle(MemoryLayout.PathElement.groupElement("ws_col"));
Linker linker = Linker.nativeLinker();
ioctl = linker.downcallHandle(
linker.defaultLookup().find("ioctl").get(),
FunctionDescriptor.of(
ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.ADDRESS),
Linker.Option.firstVariadicArg(2));
isatty = linker.downcallHandle(
linker.defaultLookup().find("isatty").get(),
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT));
}

@Override
public String getProviderName() {
return "ffm";
}

@Override
public CLibrary getCLibrary() {
return new CLibrary() {
static final int TIOCGWINSZ;

static {
String osName = System.getProperty("os.name");
if (osName.startsWith("Linux")) {
String arch = System.getProperty("os.arch");
boolean isMipsPpcOrSparc =
arch.startsWith("mips") || arch.startsWith("ppc") || arch.startsWith("sparc");
TIOCGWINSZ = isMipsPpcOrSparc ? 0x40087468 : 0x00005413;
} else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) {
int _TIOC = ('T' << 8);
TIOCGWINSZ = (_TIOC | 104);
} else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
TIOCGWINSZ = 0x40087468;
} else if (osName.startsWith("FreeBSD")) {
TIOCGWINSZ = 0x40087468;
} else {
throw new UnsupportedOperationException();
}
}

@Override
public short getTerminalWidth(int fd) {
MemorySegment segment = Arena.ofAuto().allocate(wsLayout);
try {
int res = (int) ioctl.invoke(fd, (long) TIOCGWINSZ, segment);
return (short) ws_col.get(segment);
} catch (Throwable e) {
throw new RuntimeException("Unable to ioctl(TIOCGWINSZ)", e);
}
}

@Override
public int isTty(int fd) {
try {
return (int) isatty.invoke(fd);
} catch (Throwable e) {
throw new RuntimeException("Unable to call isatty", e);
}
}
};
if (OSInfo.isWindows()) {
return new WindowsCLibrary();
} else {
return new PosixCLibrary();
}
}

@Override
Expand All @@ -118,9 +53,11 @@ public int isTty(long console) {

@Override
public int getTerminalWidth(long console) {
CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(MemorySegment.ofAddress(console), info);
return info.windowWidth();
try (Arena arena = Arena.ofConfined()) {
CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(arena);
GetConsoleScreenBufferInfo(MemorySegment.ofAddress(console), info);
return info.windowWidth();
}
}

@Override
Expand All @@ -131,8 +68,8 @@ public long getStdHandle(boolean stdout) {

@Override
public int getConsoleMode(long console, int[] mode) {
try (Arena session = Arena.ofConfined()) {
MemorySegment written = session.allocate(ValueLayout.JAVA_INT);
try (Arena arena = Arena.ofConfined()) {
MemorySegment written = arena.allocate(ValueLayout.JAVA_INT);
int res = GetConsoleMode(MemorySegment.ofAddress(console), written);
mode[0] = written.getAtIndex(ValueLayout.JAVA_INT, 0);
return res;
Expand All @@ -151,10 +88,7 @@ public int getLastError() {

@Override
public String getErrorMessage(int errorCode) {
int bufferSize = 160;
MemorySegment data = Arena.ofAuto().allocate(bufferSize);
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, null, errorCode, 0, data, bufferSize, null);
return data.getUtf8String(0).trim();
return org.fusesource.jansi.ffm.Kernel32.getErrorMessage(errorCode);
}

@Override
Expand Down
Loading

0 comments on commit 27a7bb5

Please sign in to comment.