Skip to content

Commit 9393ed6

Browse files
lahodajExE-Bossgnodet
authored
Fixing the FfmTerminal to run on JDK 22 and on Linux. (#945)
* Fixing the FffmTerminal to run on JDK 22 and on Linux. * Ensuring control characters are not re-written inadvertedly. * Update terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/FfmTerminalProvider.java Co-authored-by: ExE Boss <[email protected]> Co-authored-by: Guillaume Nodet <[email protected]>
1 parent 8775eb2 commit 9393ed6

File tree

10 files changed

+111
-49
lines changed

10 files changed

+111
-49
lines changed

.github/workflows/master-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
strategy:
3030
matrix:
3131
os: [ ubuntu-latest, ubuntu-20.04, windows-latest, macos-latest ]
32-
java: [ '21' ]
32+
java: [ '22' ]
3333
steps:
3434
- uses: actions/checkout@v4
3535

jline/pom.xml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,19 +400,18 @@
400400
</configuration>
401401
</execution>
402402
<execution>
403-
<id>jdk21</id>
403+
<id>jdk22</id>
404404
<goals>
405405
<goal>compile</goal>
406406
</goals>
407407
<configuration>
408408
<includes>
409409
<include>**/ffm/*.java</include>
410410
</includes>
411-
<release>21</release>
411+
<release>22</release>
412412
<compilerArgs>
413-
<arg>-Xlint:all,-options,-preview</arg>
413+
<arg>-Xlint:all,-options</arg>
414414
<arg>-Werror</arg>
415-
<arg>--enable-preview</arg>
416415
</compilerArgs>
417416
</configuration>
418417
</execution>

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
9191
<project.build.outputTimestamp>2024-01-23T12:21:59Z</project.build.outputTimestamp>
9292

93-
<java.build.version>21</java.build.version>
93+
<java.build.version>22</java.build.version>
9494
<java.release.version>8</java.release.version>
9595
<maven.version>3.9.6</maven.version>
9696

terminal-ffm/pom.xml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<name>JLine FFM Terminal</name>
2424

2525
<properties>
26-
<java.release.version>21</java.release.version>
26+
<java.release.version>22</java.release.version>
2727
<automatic.module.name>org.jline.terminal.ffm</automatic.module.name>
2828
</properties>
2929

@@ -56,24 +56,21 @@
5656
<groupId>org.apache.maven.plugins</groupId>
5757
<artifactId>maven-compiler-plugin</artifactId>
5858
<configuration>
59-
<release>21</release>
60-
<compilerArgs>
61-
<arg>--enable-preview</arg>
62-
</compilerArgs>
59+
<release>${java.release.version}</release>
6360
</configuration>
6461
</plugin>
6562
<plugin>
6663
<groupId>org.apache.maven.plugins</groupId>
6764
<artifactId>maven-surefire-plugin</artifactId>
6865
<configuration>
69-
<argLine>--enable-preview --enable-native-access=ALL-UNNAMED</argLine>
66+
<argLine>--enable-native-access=ALL-UNNAMED</argLine>
7067
</configuration>
7168
</plugin>
7269
<plugin>
7370
<groupId>org.apache.maven.plugins</groupId>
7471
<artifactId>maven-javadoc-plugin</artifactId>
7572
<configuration>
76-
<additionalOptions>--enable-preview --release ${java.release.version}</additionalOptions>
73+
<additionalOptions>--release ${java.release.version}</additionalOptions>
7774
</configuration>
7875
</plugin>
7976
</plugins>

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/CLibrary.java

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import java.io.InputStream;
1414
import java.lang.foreign.*;
1515
import java.lang.invoke.MethodHandle;
16+
import java.lang.invoke.MethodHandles;
17+
import java.lang.invoke.MethodType;
1618
import java.lang.invoke.VarHandle;
1719
import java.nio.file.Files;
1820
import java.nio.file.Path;
@@ -33,7 +35,7 @@
3335
import org.jline.terminal.spi.TerminalProvider;
3436
import org.jline.utils.OSUtils;
3537

36-
@SuppressWarnings("preview")
38+
@SuppressWarnings("restricted")
3739
class CLibrary {
3840

3941
private static final Logger logger = Logger.getLogger("org.jline");
@@ -51,8 +53,8 @@ static class winsize {
5153
ValueLayout.JAVA_SHORT.withName("ws_col"),
5254
ValueLayout.JAVA_SHORT,
5355
ValueLayout.JAVA_SHORT);
54-
ws_row = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ws_row"));
55-
ws_col = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("ws_col"));
56+
ws_row = FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("ws_row"));
57+
ws_col = FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("ws_col"));
5658
}
5759

5860
private final java.lang.foreign.MemorySegment seg;
@@ -97,24 +99,59 @@ static class termios {
9799
private static final VarHandle c_oflag;
98100
private static final VarHandle c_cflag;
99101
private static final VarHandle c_lflag;
102+
private static final long c_cc_offset;
100103
private static final VarHandle c_ispeed;
101104
private static final VarHandle c_ospeed;
102105

103106
static {
104-
LAYOUT = MemoryLayout.structLayout(
105-
ValueLayout.JAVA_LONG.withName("c_iflag"),
106-
ValueLayout.JAVA_LONG.withName("c_oflag"),
107-
ValueLayout.JAVA_LONG.withName("c_cflag"),
108-
ValueLayout.JAVA_LONG.withName("c_lflag"),
109-
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
110-
ValueLayout.JAVA_LONG.withName("c_ispeed"),
111-
ValueLayout.JAVA_LONG.withName("c_ospeed"));
112-
c_iflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_iflag"));
113-
c_oflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_oflag"));
114-
c_cflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_cflag"));
115-
c_lflag = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_lflag"));
116-
c_ispeed = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_ispeed"));
117-
c_ospeed = LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("c_ospeed"));
107+
if (OSUtils.IS_OSX) {
108+
LAYOUT = MemoryLayout.structLayout(
109+
ValueLayout.JAVA_LONG.withName("c_iflag"),
110+
ValueLayout.JAVA_LONG.withName("c_oflag"),
111+
ValueLayout.JAVA_LONG.withName("c_cflag"),
112+
ValueLayout.JAVA_LONG.withName("c_lflag"),
113+
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
114+
ValueLayout.JAVA_LONG.withName("c_ispeed"),
115+
ValueLayout.JAVA_LONG.withName("c_ospeed"));
116+
} else if (OSUtils.IS_LINUX) {
117+
LAYOUT = MemoryLayout.structLayout(
118+
ValueLayout.JAVA_INT.withName("c_iflag"),
119+
ValueLayout.JAVA_INT.withName("c_oflag"),
120+
ValueLayout.JAVA_INT.withName("c_cflag"),
121+
ValueLayout.JAVA_INT.withName("c_lflag"),
122+
ValueLayout.JAVA_BYTE.withName("c_line"),
123+
MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName("c_cc"),
124+
MemoryLayout.paddingLayout(3),
125+
ValueLayout.JAVA_INT.withName("c_ispeed"),
126+
ValueLayout.JAVA_INT.withName("c_ospeed"));
127+
} else {
128+
throw new IllegalStateException("Unsupported system!");
129+
}
130+
c_iflag = adjust2LinuxHandle(
131+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_iflag")));
132+
c_oflag = adjust2LinuxHandle(
133+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_oflag")));
134+
c_cflag = adjust2LinuxHandle(
135+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_cflag")));
136+
c_lflag = adjust2LinuxHandle(
137+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_lflag")));
138+
c_cc_offset = LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("c_cc"));
139+
c_ispeed = adjust2LinuxHandle(
140+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_ispeed")));
141+
c_ospeed = adjust2LinuxHandle(
142+
FfmTerminalProvider.lookupVarHandle(LAYOUT, MemoryLayout.PathElement.groupElement("c_ospeed")));
143+
}
144+
145+
private static VarHandle adjust2LinuxHandle(VarHandle v) {
146+
if (OSUtils.IS_LINUX) {
147+
MethodHandle id = MethodHandles.identity(int.class);
148+
v = MethodHandles.filterValue(
149+
v,
150+
MethodHandles.explicitCastArguments(id, MethodType.methodType(int.class, long.class)),
151+
MethodHandles.explicitCastArguments(id, MethodType.methodType(long.class, int.class)));
152+
}
153+
154+
return v;
118155
}
119156

120157
private final java.lang.foreign.MemorySegment seg;
@@ -211,14 +248,18 @@ static class termios {
211248
c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR);
212249
c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT);
213250
c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP);
214-
c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP);
251+
if (VDSUSP != (-1)) {
252+
c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP);
253+
}
215254
c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART);
216255
c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP);
217256
c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT);
218257
c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD);
219258
c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN);
220259
c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME);
221-
c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS);
260+
if (VSTATUS != (-1)) {
261+
c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS);
262+
}
222263
c_cc().copyFrom(java.lang.foreign.MemorySegment.ofArray(c_cc));
223264
}
224265

@@ -259,7 +300,7 @@ void c_lflag(long f) {
259300
}
260301

261302
java.lang.foreign.MemorySegment c_cc() {
262-
return seg.asSlice(32, 20);
303+
return seg.asSlice(c_cc_offset, 20);
263304
}
264305

265306
long c_ispeed() {
@@ -377,14 +418,18 @@ public Attributes asAttributes() {
377418
cc.put(Attributes.ControlChar.VINTR, (int) c_cc[VINTR]);
378419
cc.put(Attributes.ControlChar.VQUIT, (int) c_cc[VQUIT]);
379420
cc.put(Attributes.ControlChar.VSUSP, (int) c_cc[VSUSP]);
380-
cc.put(Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP]);
421+
if (VDSUSP != (-1)) {
422+
cc.put(Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP]);
423+
}
381424
cc.put(Attributes.ControlChar.VSTART, (int) c_cc[VSTART]);
382425
cc.put(Attributes.ControlChar.VSTOP, (int) c_cc[VSTOP]);
383426
cc.put(Attributes.ControlChar.VLNEXT, (int) c_cc[VLNEXT]);
384427
cc.put(Attributes.ControlChar.VDISCARD, (int) c_cc[VDISCARD]);
385428
cc.put(Attributes.ControlChar.VMIN, (int) c_cc[VMIN]);
386429
cc.put(Attributes.ControlChar.VTIME, (int) c_cc[VTIME]);
387-
cc.put(Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS]);
430+
if (VSTATUS != (-1)) {
431+
cc.put(Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS]);
432+
}
388433
// Return
389434
return attr;
390435
}
@@ -630,19 +675,19 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
630675
private static final int VWERASE;
631676
private static final int VKILL;
632677
private static final int VREPRINT;
633-
private static int VERASE2;
678+
private static final int VERASE2;
634679
private static final int VINTR;
635680
private static final int VQUIT;
636681
private static final int VSUSP;
637-
private static int VDSUSP;
682+
private static final int VDSUSP;
638683
private static final int VSTART;
639684
private static final int VSTOP;
640685
private static final int VLNEXT;
641686
private static final int VDISCARD;
642687
private static final int VMIN;
643-
private static int VSWTC;
688+
private static final int VSWTC;
644689
private static final int VTIME;
645-
private static int VSTATUS;
690+
private static final int VSTATUS;
646691

647692
private static final int IGNBRK;
648693
private static final int BRKINT;
@@ -784,6 +829,9 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
784829
VWERASE = 14;
785830
VLNEXT = 15;
786831
VEOL2 = 16;
832+
VERASE2 = -1;
833+
VDSUSP = -1;
834+
VSTATUS = -1;
787835

788836
IGNBRK = 0x0000001;
789837
BRKINT = 0x0000002;
@@ -906,6 +954,9 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
906954
VWERASE = 14;
907955
VLNEXT = 15;
908956
VEOL2 = 16;
957+
VERASE2 = -1;
958+
VDSUSP = -1;
959+
VSTATUS = -1;
909960

910961
IGNBRK = 0x0000001;
911962
BRKINT = 0x0000002;
@@ -1026,6 +1077,8 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
10261077
VMIN = 16;
10271078
VTIME = 17;
10281079
VSTATUS = 18;
1080+
VERASE2 = -1;
1081+
VSWTC = -1;
10291082

10301083
IGNBRK = 0x00000001;
10311084
BRKINT = 0x00000002;
@@ -1119,6 +1172,7 @@ static Pty openpty(TerminalProvider provider, Attributes attr, Size size) {
11191172
VMIN = 16;
11201173
VTIME = 17;
11211174
VSTATUS = 18;
1175+
VSWTC = -1;
11221176

11231177
IGNBRK = 0x0000001;
11241178
BRKINT = 0x0000002;

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/FfmTerminalProvider.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
import java.io.IOException;
1313
import java.io.InputStream;
1414
import java.io.OutputStream;
15+
import java.lang.foreign.MemoryLayout;
16+
import java.lang.foreign.MemoryLayout.PathElement;
17+
import java.lang.invoke.MethodHandles;
18+
import java.lang.invoke.VarHandle;
1519
import java.nio.charset.Charset;
1620

1721
import org.jline.terminal.Attributes;
@@ -115,4 +119,13 @@ public int systemStreamWidth(SystemStream stream) {
115119
public String toString() {
116120
return "TerminalProvider[" + name() + "]";
117121
}
122+
123+
static VarHandle lookupVarHandle(MemoryLayout layout, PathElement... element) {
124+
VarHandle h = layout.varHandle(element);
125+
126+
// the last parameter of the VarHandle is additional offset, hardcode zero:
127+
h = MethodHandles.insertCoordinates(h, h.coordinateTypes().size() - 1, 0L);
128+
129+
return h;
130+
}
118131
}

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/Kernel32.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import java.nio.charset.StandardCharsets;
1515
import java.util.Objects;
1616

17-
@SuppressWarnings({"unused", "preview"})
17+
@SuppressWarnings({"unused", "restricted"})
1818
final class Kernel32 {
1919

2020
public static final int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
@@ -277,8 +277,8 @@ public static INPUT_RECORD[] readConsoleInputHelper(java.lang.foreign.MemorySegm
277277
public static INPUT_RECORD[] readConsoleInputHelper(
278278
java.lang.foreign.Arena arena, java.lang.foreign.MemorySegment handle, int count, boolean peek)
279279
throws IOException {
280-
java.lang.foreign.MemorySegment inputRecordPtr = arena.allocateArray(INPUT_RECORD.LAYOUT, count);
281-
java.lang.foreign.MemorySegment length = arena.allocate(java.lang.foreign.ValueLayout.JAVA_INT, 0);
280+
java.lang.foreign.MemorySegment inputRecordPtr = arena.allocate(INPUT_RECORD.LAYOUT, count);
281+
java.lang.foreign.MemorySegment length = arena.allocate(java.lang.foreign.ValueLayout.JAVA_INT, 1);
282282
int res = peek
283283
? PeekConsoleInputW(handle, inputRecordPtr, count, length)
284284
: ReadConsoleInputW(handle, inputRecordPtr, count, length);
@@ -910,11 +910,13 @@ static <T> T requireNonNull(T obj, String symbolName) {
910910
}
911911

912912
static VarHandle varHandle(java.lang.foreign.MemoryLayout layout, String name) {
913-
return layout.varHandle(java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
913+
return FfmTerminalProvider.lookupVarHandle(
914+
layout, java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
914915
}
915916

916917
static VarHandle varHandle(java.lang.foreign.MemoryLayout layout, String e1, String name) {
917-
return layout.varHandle(
918+
return FfmTerminalProvider.lookupVarHandle(
919+
layout,
918920
java.lang.foreign.MemoryLayout.PathElement.groupElement(e1),
919921
java.lang.foreign.MemoryLayout.PathElement.groupElement(name));
920922
}

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/NativeWinConsoleWriter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
import static org.jline.terminal.impl.ffm.Kernel32.WriteConsoleW;
2020
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;
2121

22-
@SuppressWarnings("preview")
2322
class NativeWinConsoleWriter extends AbstractWindowsConsoleWriter {
2423

2524
private final java.lang.foreign.MemorySegment console = GetStdHandle(STD_OUTPUT_HANDLE);
2625

2726
@Override
2827
protected void writeConsole(char[] text, int len) throws IOException {
2928
try (java.lang.foreign.Arena arena = java.lang.foreign.Arena.ofConfined()) {
30-
java.lang.foreign.MemorySegment txt = arena.allocateArray(ValueLayout.JAVA_CHAR, text);
29+
java.lang.foreign.MemorySegment txt = arena.allocateFrom(ValueLayout.JAVA_CHAR, text);
3130
if (WriteConsoleW(console, txt, len, MemorySegment.NULL, MemorySegment.NULL) == 0) {
3231
throw new IOException("Failed to write to console: " + getLastErrorMessage());
3332
}

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/NativeWinSysTerminal.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;
3939
import static org.jline.terminal.impl.ffm.Kernel32.readConsoleInputHelper;
4040

41-
@SuppressWarnings("preview")
4241
public class NativeWinSysTerminal extends AbstractWindowsTerminal<java.lang.foreign.MemorySegment> {
4342

4443
public static NativeWinSysTerminal createTerminal(

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/WindowsAnsiWriter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import static org.jline.terminal.impl.ffm.Kernel32.SetConsoleTitleW;
3838
import static org.jline.terminal.impl.ffm.Kernel32.getLastErrorMessage;
3939

40-
@SuppressWarnings("preview")
4140
class WindowsAnsiWriter extends AnsiWriter {
4241

4342
private static final java.lang.foreign.MemorySegment console = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -401,7 +400,7 @@ protected void processDeleteLine(int optionInt) throws IOException {
401400
@Override
402401
protected void processChangeWindowTitle(String title) {
403402
try (java.lang.foreign.Arena session = java.lang.foreign.Arena.ofConfined()) {
404-
java.lang.foreign.MemorySegment str = session.allocateUtf8String(title);
403+
java.lang.foreign.MemorySegment str = session.allocateFrom(title);
405404
SetConsoleTitleW(str);
406405
}
407406
}

0 commit comments

Comments
 (0)