Skip to content

Commit

Permalink
Support for FFM on JDK 21
Browse files Browse the repository at this point in the history
- the minimal build requirement is bumped to JDK 21
- FFM is the default provider if available (JDK >= 21 with --enable-preview flag)
  • Loading branch information
gnodet committed Sep 26, 2023
1 parent cdb8d8c commit 836e464
Show file tree
Hide file tree
Showing 15 changed files with 2,275 additions and 461 deletions.
73 changes: 69 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,57 @@
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<id>enforce-java</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>21</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>${jdkTarget}</release>
</configuration>
<version>3.11.0</version>
<executions>
<execution>
<id>default-compile</id>
<configuration>
<release>${jdkTarget}</release>
</configuration>
</execution>
<execution>
<id>jdk-21</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>21</release>
<compilerArgument>--enable-preview</compilerArgument>
<compileSourceRoots>
<compileSourceRoots>src/main/java21</compileSourceRoots>
</compileSourceRoots>
</configuration>
</execution>
<execution>
<id>default-testCompile</id>
<configuration>
<release>${jdkTarget}</release>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
Expand Down Expand Up @@ -410,4 +454,25 @@
</extensions>
</build>

<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
<version>4.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
59 changes: 32 additions & 27 deletions src/main/java/org/fusesource/jansi/AnsiConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
import static org.fusesource.jansi.internal.Kernel32.STD_ERROR_HANDLE;
import static org.fusesource.jansi.internal.Kernel32.STD_OUTPUT_HANDLE;
import static org.fusesource.jansi.internal.Kernel32.SetConsoleMode;
import static org.fusesource.jansi.AnsiConsoleHelper.CLibrary.*;
import static org.fusesource.jansi.AnsiConsoleHelper.Kernel32.*;

/**
* Provides consistent access to an ANSI aware console PrintStream or an ANSI codes stripping PrintStream
Expand Down Expand Up @@ -167,6 +169,11 @@ public class AnsiConsole {
*/
public static final String JANSI_GRACEFUL = "jansi.graceful";

public static final String JANSI_PROVIDERS = "jansi.providers";
public static final String JANSI_PROVIDER_JNI = "jni";
public static final String JANSI_PROVIDER_FFM = "ffm";
public static final String JANSI_PROVIDERS_DEFAULT = JANSI_PROVIDER_FFM + "," + JANSI_PROVIDER_JNI;

/**
* @deprecated this field will be made private in a future release, use {@link #sysOut()} instead
*/
Expand Down Expand Up @@ -246,9 +253,9 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
// the library can not be loaded on unsupported platforms
final int fd = stdout ? STDOUT_FILENO : STDERR_FILENO;
try {
// If we can detect that stdout is not a tty.. then setup
// to strip the ANSI sequences..
isAtty = isatty(fd) != 0;
// If we can detect that stdout is not a tty, then setup
// to strip the ANSI sequences...
isAtty = getCLibrary().isTty(fd) != 0;
String term = System.getenv("TERM");
String emacs = System.getenv("INSIDE_EMACS");
if (isAtty && "dumb".equals(term) && emacs != null && !emacs.contains("comint")) {
Expand All @@ -273,26 +280,29 @@ private static AnsiPrintStream ansiStream(boolean stdout) {
type = withException ? AnsiType.Unsupported : AnsiType.Redirected;
installer = uninstaller = null;
width = new AnsiOutputStream.ZeroWidthSupplier();
} else if (IS_WINDOWS) {
final long console = GetStdHandle(stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
}
else if (IS_WINDOWS) {
final long console = getKernel32().getStdHandle(stdout);
final int[] mode = new int[1];
final boolean isConsole = GetConsoleMode(console, mode) != 0;
if (isConsole && SetConsoleMode(console, mode[0] | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0) {
SetConsoleMode(console, mode[0]); // set it back for now, but we know it works
final boolean isConsole = getKernel32().getConsoleMode(console, mode) != 0;
if (isConsole
&& getKernel32().setConsoleMode(console, mode[0] | ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0) {
// set it back for now, but we know it works
getKernel32().setConsoleMode(console, mode[0]);
processor = null;
type = AnsiType.VirtualTerminal;
installer = new AnsiOutputStream.IoRunnable() {
@Override
public void run() throws IOException {
virtualProcessing++;
SetConsoleMode(console, mode[0] | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
getKernel32().setConsoleMode(console, mode[0] | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
}
};
uninstaller = new AnsiOutputStream.IoRunnable() {
@Override
public void run() throws IOException {
if (--virtualProcessing == 0) {
SetConsoleMode(console, mode[0]);
getKernel32().setConsoleMode(console, mode[0]);
}
}
};
Expand All @@ -308,7 +318,7 @@ public void run() throws IOException {
AnsiProcessor proc;
AnsiType ttype;
try {
proc = new WindowsAnsiProcessor(out, console);
proc = getKernel32().newProcessor(out, console);
ttype = AnsiType.Emulation;
} catch (Throwable ignore) {
// this happens when the stdout is being redirected to a file.
Expand All @@ -320,14 +330,7 @@ public void run() throws IOException {
type = ttype;
installer = uninstaller = null;
}
width = new AnsiOutputStream.WidthSupplier() {
@Override
public int getTerminalWidth() {
CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
GetConsoleScreenBufferInfo(console, info);
return info.windowWidth();
}
};
width = () -> getKernel32().getTerminalWidth( console );
}

// We must be on some Unix variant...
Expand All @@ -336,14 +339,7 @@ public int getTerminalWidth() {
processor = null;
type = AnsiType.Native;
installer = uninstaller = null;
width = new AnsiOutputStream.WidthSupplier() {
@Override
public int getTerminalWidth() {
WinSize sz = new WinSize();
ioctl(fd, CLibrary.TIOCGWINSZ, sz);
return sz.ws_col;
}
};
width = () -> getCLibrary().getTerminalWidth( fd );
}

AnsiMode mode;
Expand Down Expand Up @@ -553,4 +549,13 @@ static synchronized void initStreams() {
initialized = true;
}
}

private static AnsiConsoleSupport.Kernel32 getKernel32() {
return AnsiConsoleSupport.getInstance().getKernel32();
}

private static AnsiConsoleSupport.CLibrary getCLibrary() {
return AnsiConsoleSupport.getInstance().getCLibrary();
}

}
65 changes: 65 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiConsoleSupport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.jansi;

import org.fusesource.jansi.io.AnsiProcessor;

import java.io.IOException;
import java.io.OutputStream;

public interface AnsiConsoleSupport {

interface CLibrary {

int STDOUT_FILENO = 1;
int STDERR_FILENO = 2;

short getTerminalWidth(int fd);

int isTty(int fd);

}

interface Kernel32 {

int isTty(long console);

int getTerminalWidth(long console);

long getStdHandle(boolean stdout);

int getConsoleMode(long console, int[] mode);

int setConsoleMode(long console, int mode);

int getLastError();

String getErrorMessage(int errorCode);

AnsiProcessor newProcessor(OutputStream os, long console) throws IOException;
}

String getProviderName();

CLibrary getCLibrary();

Kernel32 getKernel32();

static AnsiConsoleSupport getInstance() {
return AnsiConsoleSupportHolder.get();
}

}
56 changes: 56 additions & 0 deletions src/main/java/org/fusesource/jansi/AnsiConsoleSupportHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (C) 2023 the original author(s).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fusesource.jansi;

import org.fusesource.jansi.ffm.AnsiConsoleSupportFfm;
import org.fusesource.jansi.internal.AnsiConsoleSupportJni;

import static org.fusesource.jansi.AnsiConsole.JANSI_PROVIDERS;
import static org.fusesource.jansi.AnsiConsole.JANSI_PROVIDERS_DEFAULT;
import static org.fusesource.jansi.AnsiConsole.JANSI_PROVIDER_FFM;
import static org.fusesource.jansi.AnsiConsole.JANSI_PROVIDER_JNI;

class AnsiConsoleSupportHolder {
static volatile AnsiConsoleSupport instance;

static AnsiConsoleSupport get() {
if (instance == null) {
synchronized (AnsiConsoleSupportHolder.class) {
if (instance == null) {
instance = doGet();
}
}
}
return instance;
}

static AnsiConsoleSupport doGet() {
RuntimeException error = new RuntimeException("Unable to create AnsiConsoleSupport provider");
String[] providers = System.getProperty(JANSI_PROVIDERS, JANSI_PROVIDERS_DEFAULT).split(",");
for (String provider : providers) {
try {
if (JANSI_PROVIDER_FFM.equals(provider)) {
return new AnsiConsoleSupportFfm();
} else if (JANSI_PROVIDER_JNI.equals(provider)) {
return new AnsiConsoleSupportJni();
}
} catch (Throwable t) {
error.addSuppressed(t);
}
}
throw error;
}
}
Loading

0 comments on commit 836e464

Please sign in to comment.