diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/CLibrary.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/CLibrary.java
new file mode 100644
index 000000000..1a04cdfbf
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/CLibrary.java
@@ -0,0 +1,1150 @@
+/*
+ * Copyright (C) 2022 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.jline.terminal.impl.jep424;
+
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.GroupLayout;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.util.EnumMap;
+import java.util.EnumSet;
+
+import org.jline.terminal.Attributes;
+import org.jline.terminal.Size;
+import org.jline.terminal.spi.Pty;
+
+class CLibrary
+{
+ // Window sizes.
+ // @see IOCTL_TTY(2) man-page
+ static class winsize
+ {
+ static GroupLayout layout;
+ static VarHandle ws_col;
+ static VarHandle ws_row;
+
+ static
+ {
+ layout = MemoryLayout.structLayout(
+ ValueLayout.JAVA_SHORT.withName( "ws_row" ),
+ ValueLayout.JAVA_SHORT.withName( "ws_col" ),
+ ValueLayout.JAVA_SHORT,
+ ValueLayout.JAVA_SHORT
+ );
+ ws_row = layout.varHandle( MemoryLayout.PathElement.groupElement( "ws_row" ) );
+ ws_col = layout.varHandle( MemoryLayout.PathElement.groupElement( "ws_col" ) );
+ }
+
+ private final MemorySegment seg;
+
+ winsize()
+ {
+ seg = MemorySegment.allocateNative( layout, MemorySession.openImplicit() );
+ }
+
+ winsize( short ws_col, short ws_row )
+ {
+ this();
+ ws_col( ws_col );
+ ws_row( ws_row );
+ }
+
+ MemoryAddress address()
+ {
+ return seg.address();
+ }
+
+ short ws_col()
+ {
+ return (short) ws_col.get( seg.address() );
+ }
+
+ void ws_col( short col )
+ {
+ ws_col.set( seg.address(), col );
+ }
+
+ short ws_row()
+ {
+ return (short) ws_row.get( seg.address() );
+ }
+
+ void ws_row( short row )
+ {
+ ws_row.set( seg.address(), row );
+ }
+ }
+
+ // termios structure for termios functions, describing a general terminal interface that is
+ // provided to control asynchronous communications ports
+ // @see TERMIOS(3) man-page
+ static class termios
+ {
+ final static GroupLayout LAYOUT;
+ private final static VarHandle c_iflag;
+ private final static VarHandle c_oflag;
+ private final static VarHandle c_cflag;
+ private final static VarHandle c_lflag;
+ private final static VarHandle c_ispeed;
+ private final static VarHandle c_ospeed;
+
+ static
+ {
+ LAYOUT = MemoryLayout.structLayout(
+ ValueLayout.JAVA_LONG.withName( "c_iflag" ),
+ ValueLayout.JAVA_LONG.withName( "c_oflag" ),
+ ValueLayout.JAVA_LONG.withName( "c_cflag" ),
+ ValueLayout.JAVA_LONG.withName( "c_lflag" ),
+ MemoryLayout.sequenceLayout( 32, ValueLayout.JAVA_BYTE ).withName( "c_cc" ),
+ ValueLayout.JAVA_LONG.withName( "c_ispeed" ),
+ ValueLayout.JAVA_LONG.withName( "c_ospeed" )
+ );
+ c_iflag = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_iflag" ) );
+ c_oflag = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_oflag" ) );
+ c_cflag = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_cflag" ) );
+ c_lflag = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_lflag" ) );
+ c_ispeed = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_ispeed" ) );
+ c_ospeed = LAYOUT.varHandle( MemoryLayout.PathElement.groupElement( "c_ospeed" ) );
+ }
+
+ private final MemorySegment seg;
+
+ termios()
+ {
+ seg = MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() );
+ }
+
+ termios( Attributes t )
+ {
+ this();
+ // Input flags
+ long c_iflag = 0;
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IGNBRK ), IGNBRK, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.BRKINT ), BRKINT, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IGNPAR ), IGNPAR, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.PARMRK ), PARMRK, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.INPCK ), INPCK, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.ISTRIP ), ISTRIP, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.INLCR ), INLCR, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IGNCR ), IGNCR, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.ICRNL ), ICRNL, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IXON ), IXON, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IXOFF ), IXOFF, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IXANY ), IXANY, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IMAXBEL ), IMAXBEL, c_iflag );
+ c_iflag = setFlag( t.getInputFlag( Attributes.InputFlag.IUTF8 ), IUTF8, c_iflag );
+ c_iflag( c_iflag );
+ // Output flags
+ long c_oflag = 0;
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.OPOST ), OPOST, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.ONLCR ), ONLCR, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.OXTABS ), OXTABS, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.ONOEOT ), ONOEOT, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.OCRNL ), OCRNL, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.ONOCR ), ONOCR, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.ONLRET ), ONLRET, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.OFILL ), OFILL, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.NLDLY ), NLDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.TABDLY ), TABDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.CRDLY ), CRDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.FFDLY ), FFDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.BSDLY ), BSDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.VTDLY ), VTDLY, c_oflag );
+ c_oflag = setFlag( t.getOutputFlag( Attributes.OutputFlag.OFDEL ), OFDEL, c_oflag );
+ c_oflag( c_oflag );
+ // Control flags
+ long c_cflag = 0;
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CIGNORE ), CIGNORE, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CS5 ), CS5, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CS6 ), CS6, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CS7 ), CS7, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CS8 ), CS8, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CSTOPB ), CSTOPB, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CREAD ), CREAD, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.PARENB ), PARENB, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.PARODD ), PARODD, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.HUPCL ), HUPCL, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CLOCAL ), CLOCAL, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CCTS_OFLOW ), CCTS_OFLOW, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CRTS_IFLOW ), CRTS_IFLOW, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CDTR_IFLOW ), CDTR_IFLOW, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CDSR_OFLOW ), CDSR_OFLOW, c_cflag );
+ c_cflag = setFlag( t.getControlFlag( Attributes.ControlFlag.CCAR_OFLOW ), CCAR_OFLOW, c_cflag );
+ c_cflag( c_cflag );
+ // Local flags
+ long c_lflag = 0;
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHOKE ), ECHOKE, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHOE ), ECHOE, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHOK ), ECHOK, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHO ), ECHO, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHONL ), ECHONL, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHOPRT ), ECHOPRT, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ECHOCTL ), ECHOCTL, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ISIG ), ISIG, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ICANON ), ICANON, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.ALTWERASE ), ALTWERASE, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.IEXTEN ), IEXTEN, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.EXTPROC ), EXTPROC, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.TOSTOP ), TOSTOP, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.FLUSHO ), FLUSHO, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.NOKERNINFO ), NOKERNINFO, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.PENDIN ), PENDIN, c_lflag );
+ c_lflag = setFlag( t.getLocalFlag( Attributes.LocalFlag.NOFLSH ), NOFLSH, c_lflag );
+ c_lflag( c_lflag );
+ // Control chars
+ byte[] c_cc = new byte[20];
+ c_cc[VEOF] = (byte) t.getControlChar( Attributes.ControlChar.VEOF );
+ c_cc[VEOL] = (byte) t.getControlChar( Attributes.ControlChar.VEOL );
+ c_cc[VEOL2] = (byte) t.getControlChar( Attributes.ControlChar.VEOL2 );
+ c_cc[VERASE] = (byte) t.getControlChar( Attributes.ControlChar.VERASE );
+ c_cc[VWERASE] = (byte) t.getControlChar( Attributes.ControlChar.VWERASE );
+ c_cc[VKILL] = (byte) t.getControlChar( Attributes.ControlChar.VKILL );
+ c_cc[VREPRINT] = (byte) t.getControlChar( Attributes.ControlChar.VREPRINT );
+ c_cc[VINTR] = (byte) t.getControlChar( Attributes.ControlChar.VINTR );
+ c_cc[VQUIT] = (byte) t.getControlChar( Attributes.ControlChar.VQUIT );
+ c_cc[VSUSP] = (byte) t.getControlChar( Attributes.ControlChar.VSUSP );
+ c_cc[VDSUSP] = (byte) t.getControlChar( Attributes.ControlChar.VDSUSP );
+ c_cc[VSTART] = (byte) t.getControlChar( Attributes.ControlChar.VSTART );
+ c_cc[VSTOP] = (byte) t.getControlChar( Attributes.ControlChar.VSTOP );
+ c_cc[VLNEXT] = (byte) t.getControlChar( Attributes.ControlChar.VLNEXT );
+ c_cc[VDISCARD] = (byte) t.getControlChar( Attributes.ControlChar.VDISCARD );
+ c_cc[VMIN] = (byte) t.getControlChar( Attributes.ControlChar.VMIN );
+ c_cc[VTIME] = (byte) t.getControlChar( Attributes.ControlChar.VTIME );
+ c_cc[VSTATUS] = (byte) t.getControlChar( Attributes.ControlChar.VSTATUS );
+ c_cc().copyFrom( MemorySegment.ofArray( c_cc ) );
+ }
+
+ MemoryAddress address()
+ {
+ return seg.address();
+ }
+
+ long c_iflag()
+ {
+ return (long) c_iflag.get( seg );
+ }
+
+ void c_iflag( long f )
+ {
+ c_iflag.set( seg, f );
+ }
+
+ long c_oflag()
+ {
+ return (long) c_oflag.get( seg );
+ }
+
+ void c_oflag( long f )
+ {
+ c_oflag.set( seg, f );
+ }
+
+ long c_cflag()
+ {
+ return (long) c_cflag.get( seg );
+ }
+
+ void c_cflag( long f )
+ {
+ c_cflag.set( seg, f );
+ }
+
+ long c_lflag()
+ {
+ return (long) c_lflag.get( seg );
+ }
+
+ void c_lflag( long f )
+ {
+ c_lflag.set( seg, f );
+ }
+
+ MemorySegment c_cc()
+ {
+ return seg.asSlice( 32, 20 );
+ }
+
+ long c_ispeed()
+ {
+ return (long) c_ispeed.get( seg );
+ }
+
+ void c_ispeed( long f )
+ {
+ c_ispeed.set( seg, f );
+ }
+
+ long c_ospeed()
+ {
+ return (long) c_ospeed.get( seg );
+ }
+
+ void c_ospeed( long f )
+ {
+ c_ospeed.set( seg, f );
+ }
+
+ private static long setFlag( boolean flag, long value, long org )
+ {
+ return flag ? org | value : org;
+ }
+
+ private static > void addFlag( long value, EnumSet flags, T flag, int v )
+ {
+ if ( ( value & v ) != 0 )
+ {
+ flags.add( flag );
+ }
+ }
+
+ public Attributes asAttributes()
+ {
+ Attributes attr = new Attributes();
+ // Input flags
+ long c_iflag = c_iflag();
+ EnumSet iflag = attr.getInputFlags();
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IXON, IXON );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL );
+ addFlag( c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8 );
+ // Output flags
+ long c_oflag = c_oflag();
+ EnumSet oflag = attr.getOutputFlags();
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY );
+ addFlag( c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL );
+ // Control flags
+ long c_cflag = c_cflag();
+ EnumSet cflag = attr.getControlFlags();
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CS5, CS5 );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CS6, CS6 );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CS7, CS7 );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CS8, CS8 );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW );
+ addFlag( c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW );
+ // Local flags
+ long c_lflag = c_lflag();
+ EnumSet lflag = attr.getLocalFlags();
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN );
+ addFlag( c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH );
+ // Control chars
+ byte[] c_cc = c_cc().toArray( ValueLayout.JAVA_BYTE );
+ EnumMap cc = attr.getControlChars();
+ cc.put( Attributes.ControlChar.VEOF, (int) c_cc[VEOF] );
+ cc.put( Attributes.ControlChar.VEOL, (int) c_cc[VEOL] );
+ cc.put( Attributes.ControlChar.VEOL2, (int) c_cc[VEOL2] );
+ cc.put( Attributes.ControlChar.VERASE, (int) c_cc[VERASE] );
+ cc.put( Attributes.ControlChar.VWERASE, (int) c_cc[VWERASE] );
+ cc.put( Attributes.ControlChar.VKILL, (int) c_cc[VKILL] );
+ cc.put( Attributes.ControlChar.VREPRINT, (int) c_cc[VREPRINT] );
+ cc.put( Attributes.ControlChar.VINTR, (int) c_cc[VINTR] );
+ cc.put( Attributes.ControlChar.VQUIT, (int) c_cc[VQUIT] );
+ cc.put( Attributes.ControlChar.VSUSP, (int) c_cc[VSUSP] );
+ cc.put( Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP] );
+ cc.put( Attributes.ControlChar.VSTART, (int) c_cc[VSTART] );
+ cc.put( Attributes.ControlChar.VSTOP, (int) c_cc[VSTOP] );
+ cc.put( Attributes.ControlChar.VLNEXT, (int) c_cc[VLNEXT] );
+ cc.put( Attributes.ControlChar.VDISCARD, (int) c_cc[VDISCARD] );
+ cc.put( Attributes.ControlChar.VMIN, (int) c_cc[VMIN] );
+ cc.put( Attributes.ControlChar.VTIME, (int) c_cc[VTIME] );
+ cc.put( Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS] );
+ // Return
+ return attr;
+ }
+ }
+
+ static MethodHandle ioctl;
+ static MethodHandle isatty;
+ static MethodHandle openpty;
+ static MethodHandle tcsetattr;
+ static MethodHandle tcgetattr;
+ static MethodHandle ttyname_r;
+
+ static
+ {
+ // methods
+ Linker linker = Linker.nativeLinker();
+ // https://man7.org/linux/man-pages/man2/ioctl.2.html
+ ioctl = linker.downcallHandle(
+ linker.defaultLookup().lookup( "ioctl" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_LONG, ValueLayout.ADDRESS ) );
+ // https://www.man7.org/linux/man-pages/man3/isatty.3.html
+ isatty = linker.downcallHandle(
+ linker.defaultLookup().lookup( "isatty" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT ) );
+ // https://man7.org/linux/man-pages/man3/openpty.3.html
+ openpty = linker.downcallHandle(
+ linker.defaultLookup().lookup( "openpty" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS,
+ ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS ) );
+ // https://man7.org/linux/man-pages/man3/tcsetattr.3p.html
+ tcsetattr = linker.downcallHandle(
+ linker.defaultLookup().lookup( "tcsetattr" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
+ ValueLayout.JAVA_INT, ValueLayout.ADDRESS ) );
+ // https://man7.org/linux/man-pages/man3/tcgetattr.3p.html
+ tcgetattr = linker.downcallHandle(
+ linker.defaultLookup().lookup( "tcgetattr" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS ) );
+ // https://man7.org/linux/man-pages/man3/ttyname.3.html
+ ttyname_r = linker.downcallHandle(
+ linker.defaultLookup().lookup( "ttyname_r" ).get(),
+ FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
+ ValueLayout.ADDRESS, ValueLayout.JAVA_LONG ) );
+ }
+
+ static Size getTerminalSize( int fd )
+ {
+ try
+ {
+ winsize ws = new winsize();
+ int res = (int) ioctl.invoke( fd, TIOCGWINSZ, ws.address() );
+ return new Size( ws.ws_col(), ws.ws_row() );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call ioctl(TIOCGWINSZ)", e );
+ }
+ }
+
+ static void setTerminalSize( int fd, Size size )
+ {
+ try
+ {
+ winsize ws = new winsize();
+ ws.ws_row( (short) size.getRows() );
+ ws.ws_col( (short) size.getColumns() );
+ int res = (int) ioctl.invoke( fd, TIOCSWINSZ, ws.address() );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call ioctl(TIOCGWINSZ)", e );
+ }
+ }
+
+ static Attributes getAttributes( int fd )
+ {
+ try
+ {
+ termios t = new termios();
+ int res = (int) tcgetattr.invoke( fd, t.address() );
+ return t.asAttributes();
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call tcgetattr()", e );
+ }
+ }
+
+ static void setAttributes( int fd, Attributes attr )
+ {
+ try
+ {
+ termios t = new termios( attr );
+ int res = (int) tcsetattr.invoke( fd, TCSANOW, t.address() );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call tcsetattr()", e );
+ }
+ }
+
+ static boolean isTty( int fd )
+ {
+ try
+ {
+ return (int) isatty.invoke( fd ) == 1;
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call isatty()", e );
+ }
+ }
+
+ static String ttyName( int fd )
+ {
+ try
+ {
+ MemorySegment buf = MemorySegment.allocateNative( 64, MemorySession.openImplicit() );
+ int res = (int) ttyname_r.invoke( fd, buf, buf.byteSize() );
+ byte[] data = buf.toArray( ValueLayout.JAVA_BYTE );
+ int len = 0;
+ while ( data[len] != 0 )
+ {
+ len++;
+ }
+ return new String( data, 0, len );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call ttyname_r()", e );
+ }
+ }
+
+ static Pty openpty( Attributes attr, Size size )
+ {
+ try
+ {
+ winsize ws = new winsize();
+ termios t = new termios();
+
+ int[] master = new int[1];
+ int[] slave = new int[1];
+ byte[] buf = new byte[64];
+ int res = (int) openpty.invoke( master, slave, buf,
+ attr != null ? new termios( attr ) : null,
+ size != null ? new winsize( (short) size.getRows(), (short) size.getColumns() ) : null );
+ int len = 0;
+ while ( buf[len] != 0 )
+ {
+ len++;
+ }
+ String device = new String( buf, 0, len );
+ return new NativePty( master[0],
+ NativePty.newDescriptor( master[0] ), slave[0],
+ NativePty.newDescriptor( slave[0] ), device );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to call openpty()", e );
+ }
+ }
+
+ // CONSTANTS
+
+ private final static int TIOCGWINSZ;
+ private final static int TIOCSWINSZ;
+
+ private final static int TCSANOW;
+ private static int TCSADRAIN;
+ private static int TCSAFLUSH;
+
+ private final static int VEOF;
+ private final static int VEOL;
+ private final static int VEOL2;
+ private final static int VERASE;
+ private final static int VWERASE;
+ private final static int VKILL;
+ private final static int VREPRINT;
+ private static int VERASE2;
+ private final static int VINTR;
+ private final static int VQUIT;
+ private final static int VSUSP;
+ private static int VDSUSP;
+ private final static int VSTART;
+ private final static int VSTOP;
+ private final static int VLNEXT;
+ private final static int VDISCARD;
+ private final static int VMIN;
+ private static int VSWTC;
+ private final static int VTIME;
+ private static int VSTATUS;
+
+ private final static int IGNBRK;
+ private final static int BRKINT;
+ private final static int IGNPAR;
+ private final static int PARMRK;
+ private final static int INPCK;
+ private final static int ISTRIP;
+ private final static int INLCR;
+ private final static int IGNCR;
+ private final static int ICRNL;
+ private static int IUCLC;
+ private final static int IXON;
+ private final static int IXOFF;
+ private final static int IXANY;
+ private final static int IMAXBEL;
+ private static int IUTF8;
+
+ private final static int OPOST;
+ private static int OLCUC;
+ private final static int ONLCR;
+ private static int OXTABS;
+ private static int NLDLY;
+ private static int NL0;
+ private static int NL1;
+ private final static int TABDLY;
+ private static int TAB0;
+ private static int TAB1;
+ private static int TAB2;
+ private static int TAB3;
+ private static int CRDLY;
+ private static int CR0;
+ private static int CR1;
+ private static int CR2;
+ private static int CR3;
+ private static int FFDLY;
+ private static int FF0;
+ private static int FF1;
+ private static int XTABS;
+ private static int BSDLY;
+ private static int BS0;
+ private static int BS1;
+ private static int VTDLY;
+ private static int VT0;
+ private static int VT1;
+ private static int CBAUD;
+ private static int B0;
+ private static int B50;
+ private static int B75;
+ private static int B110;
+ private static int B134;
+ private static int B150;
+ private static int B200;
+ private static int B300;
+ private static int B600;
+ private static int B1200;
+ private static int B1800;
+ private static int B2400;
+ private static int B4800;
+ private static int B9600;
+ private static int B19200;
+ private static int B38400;
+ private static int EXTA;
+ private static int EXTB;
+ private static int OFDEL;
+ private static int ONOEOT;
+ private final static int OCRNL;
+ private static int ONOCR;
+ private final static int ONLRET;
+ private static int OFILL;
+
+ private static int CIGNORE;
+ private static int CSIZE;
+ private final static int CS5;
+ private final static int CS6;
+ private final static int CS7;
+ private final static int CS8;
+ private final static int CSTOPB;
+ private final static int CREAD;
+ private final static int PARENB;
+ private final static int PARODD;
+ private final static int HUPCL;
+ private final static int CLOCAL;
+ private static int CCTS_OFLOW;
+ private static int CRTS_IFLOW;
+ private static int CDTR_IFLOW;
+ private static int CDSR_OFLOW;
+ private static int CCAR_OFLOW;
+
+ private final static int ECHOKE;
+ private final static int ECHOE;
+ private final static int ECHOK;
+ private final static int ECHO;
+ private final static int ECHONL;
+ private final static int ECHOPRT;
+ private final static int ECHOCTL;
+ private final static int ISIG;
+ private final static int ICANON;
+ private static int XCASE;
+ private static int ALTWERASE;
+ private final static int IEXTEN;
+ private final static int EXTPROC;
+ private final static int TOSTOP;
+ private final static int FLUSHO;
+ private static int NOKERNINFO;
+ private final static int PENDIN;
+ private final static int NOFLSH;
+
+ static
+ {
+ String osName = System.getProperty( "os.name" );
+ if ( osName.startsWith( "Linux" ) )
+ {
+ String arch = System.getProperty( "os.arch" );
+ boolean isMipsPpcOrSparc = arch.equals( "mips" ) || arch.equals( "mips64" )
+ || arch.equals( "mipsel" ) || arch.equals( "mips64el" )
+ || arch.startsWith( "ppc" ) || arch.startsWith( "sparc" );
+ TIOCGWINSZ = isMipsPpcOrSparc ? 0x40087468 : 0x00005413;
+ TIOCSWINSZ = isMipsPpcOrSparc ? 0x80087467 : 0x00005414;
+
+ TCSANOW = 0x0;
+ TCSADRAIN = 0x1;
+ TCSAFLUSH = 0x2;
+
+ VINTR = 0;
+ VQUIT = 1;
+ VERASE = 2;
+ VKILL = 3;
+ VEOF = 4;
+ VTIME = 5;
+ VMIN = 6;
+ VSWTC = 7;
+ VSTART = 8;
+ VSTOP = 9;
+ VSUSP = 10;
+ VEOL = 11;
+ VREPRINT = 12;
+ VDISCARD = 13;
+ VWERASE = 14;
+ VLNEXT = 15;
+ VEOL2 = 16;
+
+ IGNBRK = 0x0000001;
+ BRKINT = 0x0000002;
+ IGNPAR = 0x0000004;
+ PARMRK = 0x0000008;
+ INPCK = 0x0000010;
+ ISTRIP = 0x0000020;
+ INLCR = 0x0000040;
+ IGNCR = 0x0000080;
+ ICRNL = 0x0000100;
+ IUCLC = 0x0000200;
+ IXON = 0x0000400;
+ IXANY = 0x0000800;
+ IXOFF = 0x0001000;
+ IMAXBEL = 0x0002000;
+ IUTF8 = 0x0004000;
+
+ OPOST = 0x0000001;
+ OLCUC = 0x0000002;
+ ONLCR = 0x0000004;
+ OCRNL = 0x0000008;
+ ONOCR = 0x0000010;
+ ONLRET = 0x0000020;
+ OFILL = 0x0000040;
+ OFDEL = 0x0000080;
+ NLDLY = 0x0000100;
+ NL0 = 0x0000000;
+ NL1 = 0x0000100;
+ CRDLY = 0x0000600;
+ CR0 = 0x0000000;
+ CR1 = 0x0000200;
+ CR2 = 0x0000400;
+ CR3 = 0x0000600;
+ TABDLY = 0x0001800;
+ TAB0 = 0x0000000;
+ TAB1 = 0x0000800;
+ TAB2 = 0x0001000;
+ TAB3 = 0x0001800;
+ XTABS = 0x0001800;
+ BSDLY = 0x0002000;
+ BS0 = 0x0000000;
+ BS1 = 0x0002000;
+ VTDLY = 0x0004000;
+ VT0 = 0x0000000;
+ VT1 = 0x0004000;
+ FFDLY = 0x0008000;
+ FF0 = 0x0000000;
+ FF1 = 0x0008000;
+
+ CBAUD = 0x000100f;
+ B0 = 0x0000000;
+ B50 = 0x0000001;
+ B75 = 0x0000002;
+ B110 = 0x0000003;
+ B134 = 0x0000004;
+ B150 = 0x0000005;
+ B200 = 0x0000006;
+ B300 = 0x0000007;
+ B600 = 0x0000008;
+ B1200 = 0x0000009;
+ B1800 = 0x000000a;
+ B2400 = 0x000000b;
+ B4800 = 0x000000c;
+ B9600 = 0x000000d;
+ B19200 = 0x000000e;
+ B38400 = 0x000000f;
+ EXTA = B19200;
+ EXTB = B38400;
+ CSIZE = 0x0000030;
+ CS5 = 0x0000000;
+ CS6 = 0x0000010;
+ CS7 = 0x0000020;
+ CS8 = 0x0000030;
+ CSTOPB = 0x0000040;
+ CREAD = 0x0000080;
+ PARENB = 0x0000100;
+ PARODD = 0x0000200;
+ HUPCL = 0x0000400;
+ CLOCAL = 0x0000800;
+
+ ISIG = 0x0000001;
+ ICANON = 0x0000002;
+ XCASE = 0x0000004;
+ ECHO = 0x0000008;
+ ECHOE = 0x0000010;
+ ECHOK = 0x0000020;
+ ECHONL = 0x0000040;
+ NOFLSH = 0x0000080;
+ TOSTOP = 0x0000100;
+ ECHOCTL = 0x0000200;
+ ECHOPRT = 0x0000400;
+ ECHOKE = 0x0000800;
+ FLUSHO = 0x0001000;
+ PENDIN = 0x0002000;
+ IEXTEN = 0x0008000;
+ EXTPROC = 0x0010000;
+ }
+ else if ( osName.startsWith( "Solaris" ) || osName.startsWith( "SunOS" ) )
+ {
+ int _TIOC = ( 'T' << 8 );
+ TIOCGWINSZ = ( _TIOC | 104 );
+ TIOCSWINSZ = ( _TIOC | 103 );
+
+ TCSANOW = 0x0;
+ TCSADRAIN = 0x1;
+ TCSAFLUSH = 0x2;
+
+ VINTR = 0;
+ VQUIT = 1;
+ VERASE = 2;
+ VKILL = 3;
+ VEOF = 4;
+ VTIME = 5;
+ VMIN = 6;
+ VSWTC = 7;
+ VSTART = 8;
+ VSTOP = 9;
+ VSUSP = 10;
+ VEOL = 11;
+ VREPRINT = 12;
+ VDISCARD = 13;
+ VWERASE = 14;
+ VLNEXT = 15;
+ VEOL2 = 16;
+
+ IGNBRK = 0x0000001;
+ BRKINT = 0x0000002;
+ IGNPAR = 0x0000004;
+ PARMRK = 0x0000010;
+ INPCK = 0x0000020;
+ ISTRIP = 0x0000040;
+ INLCR = 0x0000100;
+ IGNCR = 0x0000200;
+ ICRNL = 0x0000400;
+ IUCLC = 0x0001000;
+ IXON = 0x0002000;
+ IXANY = 0x0004000;
+ IXOFF = 0x0010000;
+ IMAXBEL = 0x0020000;
+ IUTF8 = 0x0040000;
+
+ OPOST = 0x0000001;
+ OLCUC = 0x0000002;
+ ONLCR = 0x0000004;
+ OCRNL = 0x0000010;
+ ONOCR = 0x0000020;
+ ONLRET = 0x0000040;
+ OFILL = 0x0000100;
+ OFDEL = 0x0000200;
+ NLDLY = 0x0000400;
+ NL0 = 0x0000000;
+ NL1 = 0x0000400;
+ CRDLY = 0x0003000;
+ CR0 = 0x0000000;
+ CR1 = 0x0001000;
+ CR2 = 0x0002000;
+ CR3 = 0x0003000;
+ TABDLY = 0x0014000;
+ TAB0 = 0x0000000;
+ TAB1 = 0x0004000;
+ TAB2 = 0x0010000;
+ TAB3 = 0x0014000;
+ XTABS = 0x0014000;
+ BSDLY = 0x0020000;
+ BS0 = 0x0000000;
+ BS1 = 0x0020000;
+ VTDLY = 0x0040000;
+ VT0 = 0x0000000;
+ VT1 = 0x0040000;
+ FFDLY = 0x0100000;
+ FF0 = 0x0000000;
+ FF1 = 0x0100000;
+
+ CBAUD = 0x0010017;
+ B0 = 0x0000000;
+ B50 = 0x0000001;
+ B75 = 0x0000002;
+ B110 = 0x0000003;
+ B134 = 0x0000004;
+ B150 = 0x0000005;
+ B200 = 0x0000006;
+ B300 = 0x0000007;
+ B600 = 0x0000010;
+ B1200 = 0x0000011;
+ B1800 = 0x0000012;
+ B2400 = 0x0000013;
+ B4800 = 0x0000014;
+ B9600 = 0x0000015;
+ B19200 = 0x0000016;
+ B38400 = 0x0000017;
+ EXTA = 0xB19200;
+ EXTB = 0xB38400;
+ CSIZE = 0x0000060;
+ CS5 = 0x0000000;
+ CS6 = 0x0000020;
+ CS7 = 0x0000040;
+ CS8 = 0x0000060;
+ CSTOPB = 0x0000100;
+ CREAD = 0x0000200;
+ PARENB = 0x0000400;
+ PARODD = 0x0001000;
+ HUPCL = 0x0002000;
+ CLOCAL = 0x0004000;
+
+ ISIG = 0x0000001;
+ ICANON = 0x0000002;
+ XCASE = 0x0000004;
+ ECHO = 0x0000010;
+ ECHOE = 0x0000020;
+ ECHOK = 0x0000040;
+ ECHONL = 0x0000100;
+ NOFLSH = 0x0000200;
+ TOSTOP = 0x0000400;
+ ECHOCTL = 0x0001000;
+ ECHOPRT = 0x0002000;
+ ECHOKE = 0x0004000;
+ FLUSHO = 0x0010000;
+ PENDIN = 0x0040000;
+ IEXTEN = 0x0100000;
+ EXTPROC = 0x0200000;
+ }
+ else if ( osName.startsWith( "Mac" ) || osName.startsWith( "Darwin" ) )
+ {
+ TIOCGWINSZ = 0x40087468;
+ TIOCSWINSZ = 0x80087467;
+
+ TCSANOW = 0x00000000;
+
+ VEOF = 0;
+ VEOL = 1;
+ VEOL2 = 2;
+ VERASE = 3;
+ VWERASE = 4;
+ VKILL = 5;
+ VREPRINT = 6;
+ VINTR = 8;
+ VQUIT = 9;
+ VSUSP = 10;
+ VDSUSP = 11;
+ VSTART = 12;
+ VSTOP = 13;
+ VLNEXT = 14;
+ VDISCARD = 15;
+ VMIN = 16;
+ VTIME = 17;
+ VSTATUS = 18;
+
+ IGNBRK = 0x00000001;
+ BRKINT = 0x00000002;
+ IGNPAR = 0x00000004;
+ PARMRK = 0x00000008;
+ INPCK = 0x00000010;
+ ISTRIP = 0x00000020;
+ INLCR = 0x00000040;
+ IGNCR = 0x00000080;
+ ICRNL = 0x00000100;
+ IXON = 0x00000200;
+ IXOFF = 0x00000400;
+ IXANY = 0x00000800;
+ IMAXBEL = 0x00002000;
+ IUTF8 = 0x00004000;
+
+ OPOST = 0x00000001;
+ ONLCR = 0x00000002;
+ OXTABS = 0x00000004;
+ ONOEOT = 0x00000008;
+ OCRNL = 0x00000010;
+ ONOCR = 0x00000020;
+ ONLRET = 0x00000040;
+ OFILL = 0x00000080;
+ NLDLY = 0x00000300;
+ TABDLY = 0x00000c04;
+ CRDLY = 0x00003000;
+ FFDLY = 0x00004000;
+ BSDLY = 0x00008000;
+ VTDLY = 0x00010000;
+ OFDEL = 0x00020000;
+
+ CIGNORE = 0x00000001;
+ CS5 = 0x00000000;
+ CS6 = 0x00000100;
+ CS7 = 0x00000200;
+ CS8 = 0x00000300;
+ CSTOPB = 0x00000400;
+ CREAD = 0x00000800;
+ PARENB = 0x00001000;
+ PARODD = 0x00002000;
+ HUPCL = 0x00004000;
+ CLOCAL = 0x00008000;
+ CCTS_OFLOW = 0x00010000;
+ CRTS_IFLOW = 0x00020000;
+ CDTR_IFLOW = 0x00040000;
+ CDSR_OFLOW = 0x00080000;
+ CCAR_OFLOW = 0x00100000;
+
+ ECHOKE = 0x00000001;
+ ECHOE = 0x00000002;
+ ECHOK = 0x00000004;
+ ECHO = 0x00000008;
+ ECHONL = 0x00000010;
+ ECHOPRT = 0x00000020;
+ ECHOCTL = 0x00000040;
+ ISIG = 0x00000080;
+ ICANON = 0x00000100;
+ ALTWERASE = 0x00000200;
+ IEXTEN = 0x00000400;
+ EXTPROC = 0x00000800;
+ TOSTOP = 0x00400000;
+ FLUSHO = 0x00800000;
+ NOKERNINFO = 0x02000000;
+ PENDIN = 0x20000000;
+ NOFLSH = 0x80000000;
+ }
+ else if ( osName.startsWith( "FreeBSD" ) )
+ {
+ TIOCGWINSZ = 0x40087468;
+ TIOCSWINSZ = 0x80087467;
+
+ TCSANOW = 0x0;
+ TCSADRAIN = 0x1;
+ TCSAFLUSH = 0x2;
+
+ VEOF = 0;
+ VEOL = 1;
+ VEOL2 = 2;
+ VERASE = 3;
+ VWERASE = 4;
+ VKILL = 5;
+ VREPRINT = 6;
+ VERASE2 = 7;
+ VINTR = 8;
+ VQUIT = 9;
+ VSUSP = 10;
+ VDSUSP = 11;
+ VSTART = 12;
+ VSTOP = 13;
+ VLNEXT = 14;
+ VDISCARD = 15;
+ VMIN = 16;
+ VTIME = 17;
+ VSTATUS = 18;
+
+ IGNBRK = 0x0000001;
+ BRKINT = 0x0000002;
+ IGNPAR = 0x0000004;
+ PARMRK = 0x0000008;
+ INPCK = 0x0000010;
+ ISTRIP = 0x0000020;
+ INLCR = 0x0000040;
+ IGNCR = 0x0000080;
+ ICRNL = 0x0000100;
+ IXON = 0x0000200;
+ IXOFF = 0x0000400;
+ IXANY = 0x0000800;
+ IMAXBEL = 0x0002000;
+
+ OPOST = 0x0000001;
+ ONLCR = 0x0000002;
+ TABDLY = 0x0000004;
+ TAB0 = 0x0000000;
+ TAB3 = 0x0000004;
+ ONOEOT = 0x0000008;
+ OCRNL = 0x0000010;
+ ONLRET = 0x0000040;
+
+ CIGNORE = 0x0000001;
+ CSIZE = 0x0000300;
+ CS5 = 0x0000000;
+ CS6 = 0x0000100;
+ CS7 = 0x0000200;
+ CS8 = 0x0000300;
+ CSTOPB = 0x0000400;
+ CREAD = 0x0000800;
+ PARENB = 0x0001000;
+ PARODD = 0x0002000;
+ HUPCL = 0x0004000;
+ CLOCAL = 0x0008000;
+
+ ECHOKE = 0x0000001;
+ ECHOE = 0x0000002;
+ ECHOK = 0x0000004;
+ ECHO = 0x0000008;
+ ECHONL = 0x0000010;
+ ECHOPRT = 0x0000020;
+ ECHOCTL = 0x0000040;
+ ISIG = 0x0000080;
+ ICANON = 0x0000100;
+ ALTWERASE = 0x000200;
+ IEXTEN = 0x0000400;
+ EXTPROC = 0x0000800;
+ TOSTOP = 0x0400000;
+ FLUSHO = 0x0800000;
+ PENDIN = 0x2000000;
+ NOFLSH = 0x8000000;
+ }
+ else
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/Jep424TerminalProvider.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/Jep424TerminalProvider.java
index 80573b87f..843f3aac2 100644
--- a/terminal/src/main/java/org/jline/terminal/impl/jep424/Jep424TerminalProvider.java
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/Jep424TerminalProvider.java
@@ -16,30 +16,14 @@
package org.jline.terminal.impl.jep424;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.lang.foreign.FunctionDescriptor;
-import java.lang.foreign.GroupLayout;
-import java.lang.foreign.Linker;
-import java.lang.foreign.MemoryAddress;
-import java.lang.foreign.MemoryLayout;
-import java.lang.foreign.MemorySegment;
-import java.lang.foreign.MemorySession;
-import java.lang.foreign.ValueLayout;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.VarHandle;
-import java.lang.reflect.Constructor;
import java.nio.charset.Charset;
-import java.util.EnumMap;
-import java.util.EnumSet;
import org.jline.terminal.Attributes;
import org.jline.terminal.Size;
import org.jline.terminal.Terminal;
-import org.jline.terminal.impl.AbstractPty;
import org.jline.terminal.impl.PosixPtyTerminal;
import org.jline.terminal.impl.PosixSysTerminal;
import org.jline.terminal.spi.Pty;
@@ -56,13 +40,12 @@ public String name()
}
@Override
- public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
+ public Terminal sysTerminal(String name, String type, boolean ansiPassThrough, Charset encoding,
boolean nativeSignals, Terminal.SignalHandler signalHandler, boolean paused,
Stream consoleStream) throws IOException {
if ( OSUtils.IS_WINDOWS) {
-// return NativeWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding,
-// nativeSignals, signalHandler, paused, consoleStream);
- throw new UnsupportedOperationException();
+ return NativeWinSysTerminal.createTerminal(name, type, ansiPassThrough, encoding,
+ nativeSignals, signalHandler, paused, consoleStream);
} else {
Pty pty = new NativePty(-1, null, 0, FileDescriptor.in,
consoleStream == Stream.Output ? 1 : 2,
@@ -91,8 +74,7 @@ public boolean isSystemStream(Stream stream) {
}
public boolean isWindowsSystemStream(Stream stream) {
- //return NativeWinSysTerminal.isWindowsSystemStream(stream);
- throw new UnsupportedOperationException();
+ return NativeWinSysTerminal.isWindowsSystemStream(stream);
}
public boolean isPosixSystemStream(Stream stream) {
@@ -104,1442 +86,5 @@ public String systemStreamName(Stream stream) {
return NativePty.posixSystemStreamName(stream);
}
- static class NativePty extends AbstractPty
- {
- private final int master;
- private final int slave;
- private final int slaveOut;
- private final String name;
- private final FileDescriptor masterFD;
- private final FileDescriptor slaveFD;
- private final FileDescriptor slaveOutFD;
-
- public NativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name) {
- this(master, masterFD, slave, slaveFD, slave, slaveFD, name);
- }
-
- public NativePty(int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, int slaveOut, FileDescriptor slaveOutFD, String name) {
- this.master = master;
- this.slave = slave;
- this.slaveOut = slaveOut;
- this.name = name;
- this.masterFD = masterFD;
- this.slaveFD = slaveFD;
- this.slaveOutFD = slaveOutFD;
- }
-
- @Override
- public void close() throws IOException {
- if (master > 0) {
- getMasterInput().close();
- }
- if (slave > 0) {
- getSlaveInput().close();
- }
- }
-
- public int getMaster() {
- return master;
- }
-
- public int getSlave() {
- return slave;
- }
-
- public int getSlaveOut() {
- return slaveOut;
- }
-
- public String getName() {
- return name;
- }
-
- public FileDescriptor getMasterFD() {
- return masterFD;
- }
-
- public FileDescriptor getSlaveFD() {
- return slaveFD;
- }
-
- public FileDescriptor getSlaveOutFD() {
- return slaveOutFD;
- }
-
- public InputStream getMasterInput() {
- return new FileInputStream(getMasterFD());
- }
-
- public OutputStream getMasterOutput() {
- return new FileOutputStream(getMasterFD());
- }
-
- protected InputStream doGetSlaveInput() {
- return new FileInputStream(getSlaveFD());
- }
-
- public OutputStream getSlaveOutput() {
- return new FileOutputStream(getSlaveOutFD());
- }
-
- @Override
- public Attributes getAttr() throws IOException {
- return CLibrary.getAttributes(slave);
- }
-
- @Override
- protected void doSetAttr(Attributes attr) throws IOException {
- CLibrary.setAttributes(slave, attr);
- }
-
- @Override
- public Size getSize() throws IOException {
- return CLibrary.getTerminalSize(slave);
- }
-
- @Override
- public void setSize(Size size) throws IOException {
- CLibrary.setTerminalSize(slave, size);
- }
-
- @Override
- public String toString() {
- return "NativePty[" + getName() + "]";
- }
-
- protected static FileDescriptor newDescriptor(int fd) {
- try {
- Constructor cns = FileDescriptor.class.getDeclaredConstructor(int.class);
- cns.setAccessible(true);
- return cns.newInstance(fd);
- } catch (Throwable e) {
- throw new RuntimeException("Unable to create FileDescriptor", e);
- }
- }
-
- public static boolean isPosixSystemStream(TerminalProvider.Stream stream) {
- return switch ( stream ) {
- case Input -> CLibrary.isTty( 0 );
- case Output -> CLibrary.isTty( 1 );
- case Error -> CLibrary.isTty( 2 );
- };
- }
-
- public static String posixSystemStreamName(TerminalProvider.Stream stream) {
- return switch ( stream ) {
- case Input -> CLibrary.ttyName( 0 );
- case Output -> CLibrary.ttyName( 1 );
- case Error -> CLibrary.ttyName( 2 );
- };
- }
- }
-
- /*
- public static class NativeWinSysTerminal extends AbstractWindowsTerminal
- {
-
- private static final long consoleIn = Kernel32.GetStdHandle( STD_INPUT_HANDLE );
- private static final long consoleOut = Kernel32.GetStdHandle( STD_OUTPUT_HANDLE );
- private static final long consoleErr = Kernel32.GetStdHandle( STD_ERROR_HANDLE );
-
- public static NativeWinSysTerminal createTerminal( String name, String type, boolean ansiPassThrough,
- Charset encoding,
- boolean nativeSignals, SignalHandler signalHandler,
- boolean paused,
- TerminalProvider.Stream consoleStream ) throws IOException
- {
- Writer writer;
- int[] mode = new int[1];
- long console;
- switch ( consoleStream )
- {
- case Output:
- console = consoleOut;
- break;
- case Error:
- console = consoleErr;
- break;
- default:
- throw new IllegalArgumentException( "Unsupport stream for console: " + consoleStream );
- }
- if ( ansiPassThrough )
- {
- if ( type == null )
- {
- type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
- }
- writer = new JansiWinConsoleWriter();
- }
- else
- {
- if ( Kernel32.GetConsoleMode( console, mode ) == 0 )
- {
- throw new IOException( "Failed to get console mode: " + getLastErrorMessage() );
- }
- if ( Kernel32.SetConsoleMode( console,
- mode[0] | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING ) != 0 )
- {
- if ( type == null )
- {
- type = TYPE_WINDOWS_VTP;
- }
- writer = new JansiWinConsoleWriter();
- }
- else if ( OSUtils.IS_CONEMU )
- {
- if ( type == null )
- {
- type = TYPE_WINDOWS_CONEMU;
- }
- writer = new JansiWinConsoleWriter();
- }
- else
- {
- if ( type == null )
- {
- type = TYPE_WINDOWS;
- }
- writer = new WindowsAnsiWriter( new BufferedWriter( new JansiWinConsoleWriter() ) );
- }
- }
- if ( Kernel32.GetConsoleMode( consoleIn, mode ) == 0 )
- {
- throw new IOException( "Failed to get console mode: " + getLastErrorMessage() );
- }
- WinSysTerminal terminal = new WinSysTerminal( writer, name, type, encoding, nativeSignals,
- signalHandler, consoleIn, console );
- // Start input pump thread
- if ( !paused )
- {
- terminal.resume();
- }
- return terminal;
- }
-
- public static boolean isWindowsSystemStream( JansiSupport.Stream stream )
- {
- int[] mode = new int[1];
- long console;
- switch ( stream )
- {
- case Input:
- console = consoleIn;
- break;
- case Output:
- console = consoleOut;
- break;
- case Error:
- console = consoleErr;
- break;
- default:
- return false;
- }
- return Kernel32.GetConsoleMode( console, mode ) != 0;
- }
-
- private long console;
- private long outputHandle;
-
- WinSysTerminal( Writer writer, String name, String type, Charset encoding, boolean nativeSignals,
- SignalHandler signalHandler,
- long console, long outputHandle ) throws IOException
- {
- super( writer, name, type, encoding, nativeSignals, signalHandler );
- this.console = console;
- this.outputHandle = outputHandle;
- }
-
- @Override
- protected int getConsoleMode()
- {
- int[] mode = new int[1];
- if ( Kernel32.GetConsoleMode( console, mode ) == 0 )
- {
- return -1;
- }
- return mode[0];
- }
-
- @Override
- protected void setConsoleMode( int mode )
- {
- Kernel32.SetConsoleMode( console, mode );
- }
-
- public Size getSize()
- {
- Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
- Kernel32.GetConsoleScreenBufferInfo( outputHandle, info );
- return new Size( info.windowWidth(), info.windowHeight() );
- }
-
- @Override
- public Size getBufferSize()
- {
- Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO();
- Kernel32.GetConsoleScreenBufferInfo( outputHandle, info );
- return new Size( info.size.x, info.size.y );
- }
-
- protected boolean processConsoleInput() throws IOException
- {
- Kernel32.INPUT_RECORD[] events;
- if ( console != Kernel32.INVALID_HANDLE_VALUE
- && Kernel32.WaitForSingleObject( console, 100 ) == 0 )
- {
- events = readConsoleInputHelper( console, 1, false );
- }
- else
- {
- return false;
- }
-
- boolean flush = false;
- for ( Kernel32.INPUT_RECORD event : events )
- {
- if ( event.eventType == Kernel32.INPUT_RECORD.KEY_EVENT )
- {
- Kernel32.KEY_EVENT_RECORD keyEvent = event.keyEvent;
- processKeyEvent( keyEvent.keyDown, keyEvent.keyCode, keyEvent.uchar, keyEvent.controlKeyState );
- flush = true;
- }
- else if ( event.eventType == Kernel32.INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT )
- {
- raise( Signal.WINCH );
- }
- else if ( event.eventType == Kernel32.INPUT_RECORD.MOUSE_EVENT )
- {
- processMouseEvent( event.mouseEvent );
- flush = true;
- }
- else if ( event.eventType == Kernel32.INPUT_RECORD.FOCUS_EVENT )
- {
- processFocusEvent( event.focusEvent.setFocus );
- }
- }
-
- return flush;
- }
-
- private char[] focus = new char[] {'\033', '[', ' '};
-
- private void processFocusEvent( boolean hasFocus ) throws IOException
- {
- if ( focusTracking )
- {
- focus[2] = hasFocus ? 'I' : 'O';
- slaveInputPipe.write( focus );
- }
- }
-
- private char[] mouse = new char[] {'\033', '[', 'M', ' ', ' ', ' '};
-
- private void processMouseEvent( Kernel32.MOUSE_EVENT_RECORD mouseEvent ) throws IOException
- {
- int dwEventFlags = mouseEvent.eventFlags;
- int dwButtonState = mouseEvent.buttonState;
- if ( tracking == MouseTracking.Off
- || tracking == MouseTracking.Normal && dwEventFlags == Kernel32.MOUSE_EVENT_RECORD.MOUSE_MOVED
- || tracking == MouseTracking.Button && dwEventFlags == Kernel32.MOUSE_EVENT_RECORD.MOUSE_MOVED
- && dwButtonState == 0 )
- {
- return;
- }
- int cb = 0;
- dwEventFlags &= ~Kernel32.MOUSE_EVENT_RECORD.DOUBLE_CLICK; // Treat double-clicks as normal
- if ( dwEventFlags == Kernel32.MOUSE_EVENT_RECORD.MOUSE_WHEELED )
- {
- cb |= 64;
- if ( ( dwButtonState >> 16 ) < 0 )
- {
- cb |= 1;
- }
- }
- else if ( dwEventFlags == Kernel32.MOUSE_EVENT_RECORD.MOUSE_HWHEELED )
- {
- return;
- }
- else if ( ( dwButtonState & Kernel32.MOUSE_EVENT_RECORD.FROM_LEFT_1ST_BUTTON_PRESSED ) != 0 )
- {
- cb |= 0x00;
- }
- else if ( ( dwButtonState & Kernel32.MOUSE_EVENT_RECORD.RIGHTMOST_BUTTON_PRESSED ) != 0 )
- {
- cb |= 0x01;
- }
- else if ( ( dwButtonState & Kernel32.MOUSE_EVENT_RECORD.FROM_LEFT_2ND_BUTTON_PRESSED ) != 0 )
- {
- cb |= 0x02;
- }
- else
- {
- cb |= 0x03;
- }
- int cx = mouseEvent.mousePosition.x;
- int cy = mouseEvent.mousePosition.y;
- mouse[3] = (char) ( ' ' + cb );
- mouse[4] = (char) ( ' ' + cx + 1 );
- mouse[5] = (char) ( ' ' + cy + 1 );
- slaveInputPipe.write( mouse );
- }
-
- @Override
- public Cursor getCursorPosition( IntConsumer discarded )
- {
- CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
- if ( GetConsoleScreenBufferInfo( outputHandle, info ) == 0 )
- {
- throw new IOError( new IOException( "Could not get the cursor position: " + getLastErrorMessage() ) );
- }
- return new Cursor( info.cursorPosition.x, info.cursorPosition.y );
- }
-
- public void disableScrolling()
- {
- strings.remove( InfoCmp.Capability.insert_line );
- strings.remove( InfoCmp.Capability.parm_insert_line );
- strings.remove( InfoCmp.Capability.delete_line );
- strings.remove( InfoCmp.Capability.parm_delete_line );
- }
-
- static String getLastErrorMessage()
- {
- int errorCode = GetLastError();
- return getErrorMessage( errorCode );
- }
-
- static String getErrorMessage( int errorCode )
- {
- int bufferSize = 160;
- byte[] data = new byte[bufferSize];
- FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, 0, errorCode, 0, data, bufferSize, null );
- return new String( data, StandardCharsets.UTF_16LE ).trim();
- }
- }
- */
-
- static class CLibrary
- {
- // Window sizes.
- // @see IOCTL_TTY(2) man-page
- static class winsize
- {
- static GroupLayout layout;
- static VarHandle ws_col;
- static VarHandle ws_row;
- static {
- layout = MemoryLayout.structLayout(
- ValueLayout.JAVA_SHORT.withName("ws_row"),
- ValueLayout.JAVA_SHORT.withName("ws_col"),
- ValueLayout.JAVA_SHORT,
- ValueLayout.JAVA_SHORT
- );
- ws_row = layout.varHandle(MemoryLayout.PathElement.groupElement("ws_row"));
- ws_col = layout.varHandle(MemoryLayout.PathElement.groupElement("ws_col"));
- }
- private final MemorySegment seg;
- winsize() {
- seg = MemorySegment.allocateNative( layout, MemorySession.openImplicit());
- }
- winsize(short ws_col, short ws_row) {
- this();
- ws_col(ws_col);
- ws_row(ws_row);
- }
- MemoryAddress address() {
- return seg.address();
- }
- short ws_col() {
- return (short) ws_col.get(seg.address());
- }
- void ws_col(short col) {
- ws_col.set(seg.address(), col);
- }
- short ws_row() {
- return (short) ws_row.get(seg.address());
- }
- void ws_row(short row) {
- ws_row.set(seg.address(), row);
- }
- }
- // termios structure for termios functions, describing a general terminal interface that is
- // provided to control asynchronous communications ports
- // @see TERMIOS(3) man-page
- static class termios {
- static GroupLayout layout;
- static VarHandle c_iflag;
- static VarHandle c_oflag;
- static VarHandle c_cflag;
- static VarHandle c_lflag;
- static VarHandle c_ispeed;
- static VarHandle c_ospeed;
- static {
- layout = MemoryLayout.structLayout(
- ValueLayout.JAVA_LONG.withName("c_iflag"),
- ValueLayout.JAVA_LONG.withName("c_oflag"),
- ValueLayout.JAVA_LONG.withName("c_cflag"),
- ValueLayout.JAVA_LONG.withName("c_lflag"),
- MemoryLayout.sequenceLayout(32, ValueLayout.JAVA_BYTE).withName( "c_cc" ),
- ValueLayout.JAVA_LONG.withName("c_ispeed"),
- ValueLayout.JAVA_LONG.withName("c_ospeed")
- );
- c_iflag = layout.varHandle(MemoryLayout.PathElement.groupElement("c_iflag"));
- c_oflag = layout.varHandle(MemoryLayout.PathElement.groupElement("c_oflag"));
- c_cflag = layout.varHandle(MemoryLayout.PathElement.groupElement("c_cflag"));
- c_lflag = layout.varHandle(MemoryLayout.PathElement.groupElement("c_lflag"));
- c_ispeed = layout.varHandle(MemoryLayout.PathElement.groupElement("c_ispeed"));
- c_ospeed = layout.varHandle(MemoryLayout.PathElement.groupElement("c_ospeed"));
- }
- private final MemorySegment seg;
- termios() {
- seg = MemorySegment.allocateNative(layout, MemorySession.openImplicit());
- }
- termios(Attributes t) {
- this();
- // Input flags
- long c_iflag = 0;
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, c_iflag);
- c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, c_iflag);
- c_iflag(c_iflag);
- // Output flags
- long c_oflag = 0;
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, c_oflag);
- c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, c_oflag);
- c_oflag(c_oflag);
- // Control flags
- long c_cflag = 0;
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, c_cflag);
- c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, c_cflag);
- c_cflag(c_cflag);
- // Local flags
- long c_lflag = 0;
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, c_lflag);
- c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, c_lflag);
- c_lflag(c_lflag);
- // Control chars
- byte[] c_cc = new byte[20];
- c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF);
- c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL);
- c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2);
- c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE);
- c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE);
- c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL);
- c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT);
- c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR);
- c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT);
- c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP);
- c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP);
- c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART);
- c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP);
- c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT);
- c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD);
- c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN);
- c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME);
- c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS);
- c_cc().copyFrom(MemorySegment.ofArray(c_cc));
- }
- MemoryAddress address() {
- return seg.address();
- }
- long c_iflag() {
- return (long) c_iflag.get(seg);
- }
- void c_iflag(long f) {
- c_iflag.set(seg, f);
- }
- long c_oflag() {
- return (long) c_oflag.get(seg);
- }
- void c_oflag(long f) {
- c_oflag.set(seg, f);
- }
- long c_cflag() {
- return (long) c_cflag.get(seg);
- }
- void c_cflag(long f) {
- c_cflag.set(seg, f);
- }
- long c_lflag() {
- return (long) c_lflag.get(seg);
- }
- void c_lflag(long f) {
- c_lflag.set(seg, f);
- }
- MemorySegment c_cc() {
- return seg.asSlice(32, 20);
- }
- long c_ispeed() {
- return (long) c_ispeed.get(seg);
- }
- void c_ispeed(long f) {
- c_ispeed.set(seg, f);
- }
- long c_ospeed() {
- return (long) c_ospeed.get(seg);
- }
- void c_ospeed(long f) {
- c_ospeed.set(seg, f);
- }
-
- private static long setFlag(boolean flag, long value, long org) {
- return flag ? org | value : org;
- }
-
- private static > void addFlag( long value, EnumSet flags, T flag, int v) {
- if ((value & v) != 0) {
- flags.add(flag);
- }
- }
-
- public Attributes asAttributes() {
- Attributes attr = new Attributes();
- // Input flags
- long c_iflag = c_iflag();
- EnumSet iflag = attr.getInputFlags();
- addFlag(c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK);
- addFlag(c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR);
- addFlag(c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK);
- addFlag(c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK);
- addFlag(c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP);
- addFlag(c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR);
- addFlag(c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IXON, IXON);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL);
- addFlag(c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8);
- // Output flags
- long c_oflag = c_oflag();
- EnumSet oflag = attr.getOutputFlags();
- addFlag(c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY);
- addFlag(c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL);
- // Control flags
- long c_cflag = c_cflag();
- EnumSet cflag = attr.getControlFlags();
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CS5, CS5);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CS6, CS6);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CS7, CS7);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CS8, CS8);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW);
- addFlag(c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW);
- // Local flags
- long c_lflag = c_lflag();
- EnumSet lflag = attr.getLocalFlags();
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN);
- addFlag(c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH);
- // Control chars
- byte[] c_cc = c_cc().toArray(ValueLayout.JAVA_BYTE);
- EnumMap cc = attr.getControlChars();
- cc.put(Attributes.ControlChar.VEOF, (int) c_cc[VEOF]);
- cc.put(Attributes.ControlChar.VEOL, (int) c_cc[VEOL]);
- cc.put(Attributes.ControlChar.VEOL2, (int) c_cc[VEOL2]);
- cc.put(Attributes.ControlChar.VERASE, (int) c_cc[VERASE]);
- cc.put(Attributes.ControlChar.VWERASE, (int) c_cc[VWERASE]);
- cc.put(Attributes.ControlChar.VKILL, (int) c_cc[VKILL]);
- cc.put(Attributes.ControlChar.VREPRINT, (int) c_cc[VREPRINT]);
- cc.put(Attributes.ControlChar.VINTR, (int) c_cc[VINTR]);
- cc.put(Attributes.ControlChar.VQUIT, (int) c_cc[VQUIT]);
- cc.put(Attributes.ControlChar.VSUSP, (int) c_cc[VSUSP]);
- cc.put(Attributes.ControlChar.VDSUSP, (int) c_cc[VDSUSP]);
- cc.put(Attributes.ControlChar.VSTART, (int) c_cc[VSTART]);
- cc.put(Attributes.ControlChar.VSTOP, (int) c_cc[VSTOP]);
- cc.put(Attributes.ControlChar.VLNEXT, (int) c_cc[VLNEXT]);
- cc.put(Attributes.ControlChar.VDISCARD, (int) c_cc[VDISCARD]);
- cc.put(Attributes.ControlChar.VMIN, (int) c_cc[VMIN]);
- cc.put(Attributes.ControlChar.VTIME, (int) c_cc[VTIME]);
- cc.put(Attributes.ControlChar.VSTATUS, (int) c_cc[VSTATUS]);
- // Return
- return attr;
- }
- }
-
- static MethodHandle ioctl;
- static MethodHandle isatty;
- static MethodHandle openpty;
- static MethodHandle tcsetattr;
- static MethodHandle tcgetattr;
- static MethodHandle ttyname_r;
- static {
- // methods
- Linker linker = Linker.nativeLinker();
- // https://man7.org/linux/man-pages/man2/ioctl.2.html
- ioctl = linker.downcallHandle(
- linker.defaultLookup().lookup("ioctl").get(),
- FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
- ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));
- // https://www.man7.org/linux/man-pages/man3/isatty.3.html
- isatty = linker.downcallHandle(
- linker.defaultLookup().lookup("isatty").get(),
- FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT));
- // https://man7.org/linux/man-pages/man3/openpty.3.html
- openpty = linker.downcallHandle(
- linker.defaultLookup().lookup("openpty").get(),
- FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS,
- ValueLayout.ADDRESS, ValueLayout.ADDRESS, ValueLayout.ADDRESS));
- // https://man7.org/linux/man-pages/man3/tcsetattr.3p.html
- tcsetattr = linker.downcallHandle(
- linker.defaultLookup().lookup("tcsetattr").get(),
- FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
- ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
- // https://man7.org/linux/man-pages/man3/tcgetattr.3p.html
- tcgetattr = linker.downcallHandle(
- linker.defaultLookup().lookup("tcgetattr").get(),
- FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
- ValueLayout.ADDRESS));
- // https://man7.org/linux/man-pages/man3/ttyname.3.html
- ttyname_r = linker.downcallHandle(
- linker.defaultLookup().lookup( "ttyname_r" ).get(),
- FunctionDescriptor.of( ValueLayout.JAVA_INT, ValueLayout.JAVA_INT,
- ValueLayout.ADDRESS, ValueLayout.JAVA_LONG));
- }
-
- static Size getTerminalSize(int fd) {
- try {
- winsize ws = new winsize();
- int res = (int) ioctl.invoke(fd, TIOCGWINSZ, ws.address());
- return new Size( ws.ws_col(), ws.ws_row() );
- } catch (Throwable e) {
- throw new RuntimeException("Unable to ioctl(TIOCGWINSZ)", e);
- }
- }
-
- static void setTerminalSize(int fd, Size size) {
- try {
- winsize ws = new winsize();
- ws.ws_row((short) size.getRows());
- ws.ws_col((short) size.getColumns());
- int res = (int) ioctl.invoke(fd, TIOCSWINSZ, ws.address());
- } catch (Throwable e) {
- throw new RuntimeException("Unable to ioctl(TIOCGWINSZ)", e);
- }
- }
-
- static Attributes getAttributes(int fd) {
- try {
- termios t = new termios();
- int res = (int) tcgetattr.invoke(fd, t.address());
- return t.asAttributes();
- } catch (Throwable e) {
- throw new RuntimeException("Unable to ioctl(TIOCGWINSZ)", e);
- }
- }
-
- static void setAttributes(int fd, Attributes attr) {
- try {
- termios t = new termios(attr);
- int res = (int) tcsetattr.invoke(fd, TCSANOW, t.address());
- } catch (Throwable e) {
- throw new RuntimeException("Unable to tcsetattr()", e);
- }
- }
-
- static boolean isTty(int fd) {
- try {
- return (int) isatty.invoke(fd) == 1;
- } catch (Throwable e) {
- throw new RuntimeException("Unable to call isatty", e);
- }
- }
-
- static String ttyName(int fd) {
- try {
- MemorySegment buf = MemorySegment.allocateNative( 64, MemorySession.openImplicit() );
- int res = (int) ttyname_r.invoke(fd, buf, buf.byteSize());
- byte[] data = buf.toArray(ValueLayout.JAVA_BYTE);
- int len = 0;
- while (data[len] != 0) {
- len++;
- }
- return new String(data, 0, len);
- } catch (Throwable e) {
- throw new RuntimeException("Unable to call ttyname_r()", e);
- }
- }
-
- static Pty openpty(Attributes attr, Size size) {
- try {
- winsize ws = new winsize();
- termios t = new termios();
-
- int[] master = new int[1];
- int[] slave = new int[1];
- byte[] buf = new byte[64];
- int res = (int) openpty.invoke(master, slave, buf,
- attr != null ? new termios(attr) : null,
- size != null ? new winsize((short) size.getRows(), (short) size.getColumns()) : null);
- int len = 0;
- while (buf[len] != 0) {
- len++;
- }
- String device = new String(buf, 0, len);
- return new NativePty(master[0], NativePty.newDescriptor(master[0]), slave[0], NativePty.newDescriptor(slave[0]), device);
- } catch (Throwable e) {
- throw new RuntimeException("Unable to call openpty()", e);
- }
- }
-
- // CONSTANTS
-
- private static int TIOCGWINSZ;
- private static int TIOCSWINSZ;
-
- private static int TCSANOW;
- private static int TCSADRAIN;
- private static int TCSAFLUSH;
-
- private static int VEOF;
- private static int VEOL;
- private static int VEOL2;
- private static int VERASE;
- private static int VWERASE;
- private static int VKILL;
- private static int VREPRINT;
- private static int VERASE2;
- private static int VINTR;
- private static int VQUIT;
- private static int VSUSP;
- private static int VDSUSP;
- private static int VSTART;
- private static int VSTOP;
- private static int VLNEXT;
- private static int VDISCARD;
- private static int VMIN;
- private static int VSWTC;
- private static int VTIME;
- private static int VSTATUS;
-
- private static int IGNBRK;
- private static int BRKINT;
- private static int IGNPAR;
- private static int PARMRK;
- private static int INPCK;
- private static int ISTRIP;
- private static int INLCR;
- private static int IGNCR;
- private static int ICRNL;
- private static int IUCLC;
- private static int IXON;
- private static int IXOFF;
- private static int IXANY;
- private static int IMAXBEL;
- private static int IUTF8;
-
- private static int OPOST;
- private static int OLCUC;
- private static int ONLCR;
- private static int OXTABS;
- private static int NLDLY;
- private static int NL0;
- private static int NL1;
- private static int TABDLY;
- private static int TAB0;
- private static int TAB1;
- private static int TAB2;
- private static int TAB3;
- private static int CRDLY;
- private static int CR0;
- private static int CR1;
- private static int CR2;
- private static int CR3;
- private static int FFDLY;
- private static int FF0;
- private static int FF1;
- private static int XTABS;
- private static int BSDLY;
- private static int BS0;
- private static int BS1;
- private static int VTDLY;
- private static int VT0;
- private static int VT1;
- private static int CBAUD;
- private static int B0;
- private static int B50;
- private static int B75;
- private static int B110;
- private static int B134;
- private static int B150;
- private static int B200;
- private static int B300;
- private static int B600;
- private static int B1200;
- private static int B1800;
- private static int B2400;
- private static int B4800;
- private static int B9600;
- private static int B19200;
- private static int B38400;
- private static int EXTA;
- private static int EXTB;
- private static int OFDEL;
- private static int ONOEOT;
- private static int OCRNL;
- private static int ONOCR;
- private static int ONLRET;
- private static int OFILL;
-
- private static int CIGNORE;
- private static int CSIZE;
- private static int CS5;
- private static int CS6;
- private static int CS7;
- private static int CS8;
- private static int CSTOPB;
- private static int CREAD;
- private static int PARENB;
- private static int PARODD;
- private static int HUPCL;
- private static int CLOCAL;
- private static int CCTS_OFLOW;
- private static int CRTS_IFLOW;
- private static int CDTR_IFLOW;
- private static int CDSR_OFLOW;
- private static int CCAR_OFLOW;
-
- private static int ECHOKE;
- private static int ECHOE;
- private static int ECHOK;
- private static int ECHO;
- private static int ECHONL;
- private static int ECHOPRT;
- private static int ECHOCTL;
- private static int ISIG;
- private static int ICANON;
- private static int XCASE;
- private static int ALTWERASE;
- private static int IEXTEN;
- private static int EXTPROC;
- private static int TOSTOP;
- private static int FLUSHO;
- private static int NOKERNINFO;
- private static int PENDIN;
- private static int NOFLSH;
-
- static {
- String osName = System.getProperty("os.name");
- if (osName.startsWith("Linux")) {
- String arch = System.getProperty("os.arch");
- boolean isMipsPpcOrSparc = arch.equals("mips") || arch.equals("mips64")
- || arch.equals("mipsel") || arch.equals("mips64el")
- || arch.startsWith("ppc") || arch.startsWith("sparc");
- TIOCGWINSZ = isMipsPpcOrSparc ? 0x40087468 : 0x00005413;
- TIOCSWINSZ = isMipsPpcOrSparc ? 0x80087467 : 0x00005414;
-
- TCSANOW = 0x0;
- TCSADRAIN = 0x1;
- TCSAFLUSH = 0x2;
-
- VINTR = 0;
- VQUIT = 1;
- VERASE = 2;
- VKILL = 3;
- VEOF = 4;
- VTIME = 5;
- VMIN = 6;
- VSWTC = 7;
- VSTART = 8;
- VSTOP = 9;
- VSUSP = 10;
- VEOL = 11;
- VREPRINT = 12;
- VDISCARD = 13;
- VWERASE = 14;
- VLNEXT = 15;
- VEOL2 = 16;
-
- IGNBRK = 0x0000001;
- BRKINT = 0x0000002;
- IGNPAR = 0x0000004;
- PARMRK = 0x0000008;
- INPCK = 0x0000010;
- ISTRIP = 0x0000020;
- INLCR = 0x0000040;
- IGNCR = 0x0000080;
- ICRNL = 0x0000100;
- IUCLC = 0x0000200;
- IXON = 0x0000400;
- IXANY = 0x0000800;
- IXOFF = 0x0001000;
- IMAXBEL = 0x0002000;
- IUTF8 = 0x0004000;
-
- OPOST = 0x0000001;
- OLCUC = 0x0000002;
- ONLCR = 0x0000004;
- OCRNL = 0x0000008;
- ONOCR = 0x0000010;
- ONLRET = 0x0000020;
- OFILL = 0x0000040;
- OFDEL = 0x0000080;
- NLDLY = 0x0000100;
- NL0 = 0x0000000;
- NL1 = 0x0000100;
- CRDLY = 0x0000600;
- CR0 = 0x0000000;
- CR1 = 0x0000200;
- CR2 = 0x0000400;
- CR3 = 0x0000600;
- TABDLY = 0x0001800;
- TAB0 = 0x0000000;
- TAB1 = 0x0000800;
- TAB2 = 0x0001000;
- TAB3 = 0x0001800;
- XTABS = 0x0001800;
- BSDLY = 0x0002000;
- BS0 = 0x0000000;
- BS1 = 0x0002000;
- VTDLY = 0x0004000;
- VT0 = 0x0000000;
- VT1 = 0x0004000;
- FFDLY = 0x0008000;
- FF0 = 0x0000000;
- FF1 = 0x0008000;
-
- CBAUD = 0x000100f;
- B0 = 0x0000000;
- B50 = 0x0000001;
- B75 = 0x0000002;
- B110 = 0x0000003;
- B134 = 0x0000004;
- B150 = 0x0000005;
- B200 = 0x0000006;
- B300 = 0x0000007;
- B600 = 0x0000008;
- B1200 = 0x0000009;
- B1800 = 0x000000a;
- B2400 = 0x000000b;
- B4800 = 0x000000c;
- B9600 = 0x000000d;
- B19200 = 0x000000e;
- B38400 = 0x000000f;
- EXTA = B19200;
- EXTB = B38400;
- CSIZE = 0x0000030;
- CS5 = 0x0000000;
- CS6 = 0x0000010;
- CS7 = 0x0000020;
- CS8 = 0x0000030;
- CSTOPB = 0x0000040;
- CREAD = 0x0000080;
- PARENB = 0x0000100;
- PARODD = 0x0000200;
- HUPCL = 0x0000400;
- CLOCAL = 0x0000800;
-
- ISIG = 0x0000001;
- ICANON = 0x0000002;
- XCASE = 0x0000004;
- ECHO = 0x0000008;
- ECHOE = 0x0000010;
- ECHOK = 0x0000020;
- ECHONL = 0x0000040;
- NOFLSH = 0x0000080;
- TOSTOP = 0x0000100;
- ECHOCTL = 0x0000200;
- ECHOPRT = 0x0000400;
- ECHOKE = 0x0000800;
- FLUSHO = 0x0001000;
- PENDIN = 0x0002000;
- IEXTEN = 0x0008000;
- EXTPROC = 0x0010000;
- }
- else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) {
- int _TIOC = ( 'T' << 8 );
- TIOCGWINSZ = ( _TIOC | 104 );
- TIOCSWINSZ = ( _TIOC | 103 );
-
- TCSANOW = 0x0;
- TCSADRAIN = 0x1;
- TCSAFLUSH = 0x2;
-
- VINTR = 0;
- VQUIT = 1;
- VERASE = 2;
- VKILL = 3;
- VEOF = 4;
- VTIME = 5;
- VMIN = 6;
- VSWTC = 7;
- VSTART = 8;
- VSTOP = 9;
- VSUSP = 10;
- VEOL = 11;
- VREPRINT = 12;
- VDISCARD = 13;
- VWERASE = 14;
- VLNEXT = 15;
- VEOL2 = 16;
-
- IGNBRK = 0x0000001;
- BRKINT = 0x0000002;
- IGNPAR = 0x0000004;
- PARMRK = 0x0000010;
- INPCK = 0x0000020;
- ISTRIP = 0x0000040;
- INLCR = 0x0000100;
- IGNCR = 0x0000200;
- ICRNL = 0x0000400;
- IUCLC = 0x0001000;
- IXON = 0x0002000;
- IXANY = 0x0004000;
- IXOFF = 0x0010000;
- IMAXBEL = 0x0020000;
- IUTF8 = 0x0040000;
-
- OPOST = 0x0000001;
- OLCUC = 0x0000002;
- ONLCR = 0x0000004;
- OCRNL = 0x0000010;
- ONOCR = 0x0000020;
- ONLRET = 0x0000040;
- OFILL = 0x0000100;
- OFDEL = 0x0000200;
- NLDLY = 0x0000400;
- NL0 = 0x0000000;
- NL1 = 0x0000400;
- CRDLY = 0x0003000;
- CR0 = 0x0000000;
- CR1 = 0x0001000;
- CR2 = 0x0002000;
- CR3 = 0x0003000;
- TABDLY = 0x0014000;
- TAB0 = 0x0000000;
- TAB1 = 0x0004000;
- TAB2 = 0x0010000;
- TAB3 = 0x0014000;
- XTABS = 0x0014000;
- BSDLY = 0x0020000;
- BS0 = 0x0000000;
- BS1 = 0x0020000;
- VTDLY = 0x0040000;
- VT0 = 0x0000000;
- VT1 = 0x0040000;
- FFDLY = 0x0100000;
- FF0 = 0x0000000;
- FF1 = 0x0100000;
-
- CBAUD = 0x0010017;
- B0 = 0x0000000;
- B50 = 0x0000001;
- B75 = 0x0000002;
- B110 = 0x0000003;
- B134 = 0x0000004;
- B150 = 0x0000005;
- B200 = 0x0000006;
- B300 = 0x0000007;
- B600 = 0x0000010;
- B1200 = 0x0000011;
- B1800 = 0x0000012;
- B2400 = 0x0000013;
- B4800 = 0x0000014;
- B9600 = 0x0000015;
- B19200 = 0x0000016;
- B38400 = 0x0000017;
- EXTA = 0xB19200;
- EXTB = 0xB38400;
- CSIZE = 0x0000060;
- CS5 = 0x0000000;
- CS6 = 0x0000020;
- CS7 = 0x0000040;
- CS8 = 0x0000060;
- CSTOPB = 0x0000100;
- CREAD = 0x0000200;
- PARENB = 0x0000400;
- PARODD = 0x0001000;
- HUPCL = 0x0002000;
- CLOCAL = 0x0004000;
-
- ISIG = 0x0000001;
- ICANON = 0x0000002;
- XCASE = 0x0000004;
- ECHO = 0x0000010;
- ECHOE = 0x0000020;
- ECHOK = 0x0000040;
- ECHONL = 0x0000100;
- NOFLSH = 0x0000200;
- TOSTOP = 0x0000400;
- ECHOCTL = 0x0001000;
- ECHOPRT = 0x0002000;
- ECHOKE = 0x0004000;
- FLUSHO = 0x0010000;
- PENDIN = 0x0040000;
- IEXTEN = 0x0100000;
- EXTPROC = 0x0200000;
- }
- else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) {
- TIOCGWINSZ = 0x40087468;
- TIOCSWINSZ = 0x80087467;
-
- TCSANOW = 0x00000000;
-
- VEOF = 0;
- VEOL = 1;
- VEOL2 = 2;
- VERASE = 3;
- VWERASE = 4;
- VKILL = 5;
- VREPRINT = 6;
- VINTR = 8;
- VQUIT = 9;
- VSUSP = 10;
- VDSUSP = 11;
- VSTART = 12;
- VSTOP = 13;
- VLNEXT = 14;
- VDISCARD = 15;
- VMIN = 16;
- VTIME = 17;
- VSTATUS = 18;
-
- IGNBRK = 0x00000001;
- BRKINT = 0x00000002;
- IGNPAR = 0x00000004;
- PARMRK = 0x00000008;
- INPCK = 0x00000010;
- ISTRIP = 0x00000020;
- INLCR = 0x00000040;
- IGNCR = 0x00000080;
- ICRNL = 0x00000100;
- IXON = 0x00000200;
- IXOFF = 0x00000400;
- IXANY = 0x00000800;
- IMAXBEL = 0x00002000;
- IUTF8 = 0x00004000;
-
- OPOST = 0x00000001;
- ONLCR = 0x00000002;
- OXTABS = 0x00000004;
- ONOEOT = 0x00000008;
- OCRNL = 0x00000010;
- ONOCR = 0x00000020;
- ONLRET = 0x00000040;
- OFILL = 0x00000080;
- NLDLY = 0x00000300;
- TABDLY = 0x00000c04;
- CRDLY = 0x00003000;
- FFDLY = 0x00004000;
- BSDLY = 0x00008000;
- VTDLY = 0x00010000;
- OFDEL = 0x00020000;
-
- CIGNORE = 0x00000001;
- CS5 = 0x00000000;
- CS6 = 0x00000100;
- CS7 = 0x00000200;
- CS8 = 0x00000300;
- CSTOPB = 0x00000400;
- CREAD = 0x00000800;
- PARENB = 0x00001000;
- PARODD = 0x00002000;
- HUPCL = 0x00004000;
- CLOCAL = 0x00008000;
- CCTS_OFLOW = 0x00010000;
- CRTS_IFLOW = 0x00020000;
- CDTR_IFLOW = 0x00040000;
- CDSR_OFLOW = 0x00080000;
- CCAR_OFLOW = 0x00100000;
-
- ECHOKE = 0x00000001;
- ECHOE = 0x00000002;
- ECHOK = 0x00000004;
- ECHO = 0x00000008;
- ECHONL = 0x00000010;
- ECHOPRT = 0x00000020;
- ECHOCTL = 0x00000040;
- ISIG = 0x00000080;
- ICANON = 0x00000100;
- ALTWERASE = 0x00000200;
- IEXTEN = 0x00000400;
- EXTPROC = 0x00000800;
- TOSTOP = 0x00400000;
- FLUSHO = 0x00800000;
- NOKERNINFO = 0x02000000;
- PENDIN = 0x20000000;
- NOFLSH = 0x80000000;
- }
- else if (osName.startsWith("FreeBSD")) {
- TIOCGWINSZ = 0x40087468;
- TIOCSWINSZ = 0x80087467;
-
- TCSANOW = 0x0;
- TCSADRAIN = 0x1;
- TCSAFLUSH = 0x2;
-
- VEOF = 0;
- VEOL = 1;
- VEOL2 = 2;
- VERASE = 3;
- VWERASE = 4;
- VKILL = 5;
- VREPRINT = 6;
- VERASE2 = 7;
- VINTR = 8;
- VQUIT = 9;
- VSUSP = 10;
- VDSUSP = 11;
- VSTART = 12;
- VSTOP = 13;
- VLNEXT = 14;
- VDISCARD = 15;
- VMIN = 16;
- VTIME = 17;
- VSTATUS = 18;
-
- IGNBRK = 0x0000001;
- BRKINT = 0x0000002;
- IGNPAR = 0x0000004;
- PARMRK = 0x0000008;
- INPCK = 0x0000010;
- ISTRIP = 0x0000020;
- INLCR = 0x0000040;
- IGNCR = 0x0000080;
- ICRNL = 0x0000100;
- IXON = 0x0000200;
- IXOFF = 0x0000400;
- IXANY = 0x0000800;
- IMAXBEL = 0x0002000;
-
- OPOST = 0x0000001;
- ONLCR = 0x0000002;
- TABDLY = 0x0000004;
- TAB0 = 0x0000000;
- TAB3 = 0x0000004;
- ONOEOT = 0x0000008;
- OCRNL = 0x0000010;
- ONLRET = 0x0000040;
-
- CIGNORE = 0x0000001;
- CSIZE = 0x0000300;
- CS5 = 0x0000000;
- CS6 = 0x0000100;
- CS7 = 0x0000200;
- CS8 = 0x0000300;
- CSTOPB = 0x0000400;
- CREAD = 0x0000800;
- PARENB = 0x0001000;
- PARODD = 0x0002000;
- HUPCL = 0x0004000;
- CLOCAL = 0x0008000;
-
- ECHOKE = 0x0000001;
- ECHOE = 0x0000002;
- ECHOK = 0x0000004;
- ECHO = 0x0000008;
- ECHONL = 0x0000010;
- ECHOPRT = 0x0000020;
- ECHOCTL = 0x0000040;
- ISIG = 0x0000080;
- ICANON = 0x0000100;
- ALTWERASE = 0x000200;
- IEXTEN = 0x0000400;
- EXTPROC = 0x0000800;
- TOSTOP = 0x0400000;
- FLUSHO = 0x0800000;
- PENDIN = 0x2000000;
- NOFLSH = 0x8000000;
- }
- else {
- throw new UnsupportedOperationException();
- }
- }
- }
-
- static class Kernel32 {
-
-
- }
}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/Kernel32.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/Kernel32.java
new file mode 100644
index 000000000..6f351404b
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/Kernel32.java
@@ -0,0 +1,1068 @@
+/*
+ * Copyright (C) 2022 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.jline.terminal.impl.jep424;
+
+import java.io.IOException;
+import java.lang.foreign.Addressable;
+import java.lang.foreign.FunctionDescriptor;
+import java.lang.foreign.GroupLayout;
+import java.lang.foreign.Linker;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemoryLayout;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.lang.foreign.SymbolLookup;
+import java.lang.foreign.ValueLayout;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.VarHandle;
+import java.util.Objects;
+
+import static java.lang.foreign.ValueLayout.JAVA_INT;
+import static java.lang.foreign.ValueLayout.OfAddress;
+import static java.lang.foreign.ValueLayout.OfBoolean;
+import static java.lang.foreign.ValueLayout.OfByte;
+import static java.lang.foreign.ValueLayout.OfChar;
+import static java.lang.foreign.ValueLayout.OfDouble;
+import static java.lang.foreign.ValueLayout.OfFloat;
+import static java.lang.foreign.ValueLayout.OfInt;
+import static java.lang.foreign.ValueLayout.OfLong;
+import static java.lang.foreign.ValueLayout.OfShort;
+
+@SuppressWarnings( {"unused", "CopyConstructorMissesField"} )
+class Kernel32
+{
+
+ public static final int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
+
+ public static final int INVALID_HANDLE_VALUE = -1;
+ public static final int STD_INPUT_HANDLE = -10;
+ public static final int STD_OUTPUT_HANDLE = -11;
+ public static final int STD_ERROR_HANDLE = -12;
+
+ public static final int ENABLE_PROCESSED_INPUT = 0x0001;
+ public static final int ENABLE_LINE_INPUT = 0x0002;
+ public static final int ENABLE_ECHO_INPUT = 0x0004;
+ public static final int ENABLE_WINDOW_INPUT = 0x0008;
+ public static final int ENABLE_MOUSE_INPUT = 0x0010;
+ public static final int ENABLE_INSERT_MODE = 0x0020;
+ public static final int ENABLE_QUICK_EDIT_MODE = 0x0040;
+ public static final int ENABLE_EXTENDED_FLAGS = 0x0080;
+
+ public static final int RIGHT_ALT_PRESSED = 0x0001;
+ public static final int LEFT_ALT_PRESSED = 0x0002;
+ public static final int RIGHT_CTRL_PRESSED = 0x0004;
+ public static final int LEFT_CTRL_PRESSED = 0x0008;
+ public static final int SHIFT_PRESSED = 0x0010;
+
+ public static final int FOREGROUND_BLUE = 0x0001;
+ public static final int FOREGROUND_GREEN = 0x0002;
+ public static final int FOREGROUND_RED = 0x0004;
+ public static final int FOREGROUND_INTENSITY = 0x0008;
+ public static final int BACKGROUND_BLUE = 0x0010;
+ public static final int BACKGROUND_GREEN = 0x0020;
+ public static final int BACKGROUND_RED = 0x0040;
+ public static final int BACKGROUND_INTENSITY = 0x0080;
+
+ // Button state
+ public static final int FROM_LEFT_1ST_BUTTON_PRESSED = 0x0001;
+ public static final int RIGHTMOST_BUTTON_PRESSED = 0x0002;
+ public static final int FROM_LEFT_2ND_BUTTON_PRESSED = 0x0004;
+ public static final int FROM_LEFT_3RD_BUTTON_PRESSED = 0x0008;
+ public static final int FROM_LEFT_4TH_BUTTON_PRESSED = 0x0010;
+
+ // Event flags
+ public static final int MOUSE_MOVED = 0x0001;
+ public static final int DOUBLE_CLICK = 0x0002;
+ public static final int MOUSE_WHEELED = 0x0004;
+ public static final int MOUSE_HWHEELED = 0x0008;
+
+ // Event types
+ public static final short KEY_EVENT = 0x0001;
+ public static final short MOUSE_EVENT = 0x0002;
+ public static final short WINDOW_BUFFER_SIZE_EVENT = 0x0004;
+ public static final short MENU_EVENT = 0x0008;
+ public static final short FOCUS_EVENT = 0x0010;
+
+ public static int WaitForSingleObject( Addressable hHandle, int dwMilliseconds )
+ {
+ var mh$ = requireNonNull( WaitForSingleObject$MH, "WaitForSingleObject" );
+ try
+ {
+ return (int) mh$.invokeExact( hHandle, dwMilliseconds );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static MemoryAddress GetStdHandle( int nStdHandle )
+ {
+ var mh$ = requireNonNull( GetStdHandle$MH, "GetStdHandle" );
+ try
+ {
+ return (MemoryAddress) mh$.invokeExact( nStdHandle );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int FormatMessageW( int dwFlags, Addressable lpSource, int dwMessageId, int dwLanguageId,
+ Addressable lpBuffer, int nSize, Addressable Arguments )
+ {
+ var mh$ = requireNonNull( FormatMessageW$MH, "FormatMessageW" );
+ try
+ {
+ return (int) mh$.invokeExact( dwFlags, lpSource, dwMessageId, dwLanguageId, lpBuffer, nSize, Arguments );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int SetConsoleTextAttribute( Addressable hConsoleOutput, short wAttributes )
+ {
+ var mh$ = requireNonNull( SetConsoleTextAttribute$MH, "SetConsoleTextAttribute" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleOutput, wAttributes );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int SetConsoleMode( Addressable hConsoleHandle, int dwMode )
+ {
+ var mh$ = requireNonNull( SetConsoleMode$MH, "SetConsoleMode" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleHandle, dwMode );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int GetConsoleMode( Addressable hConsoleHandle, Addressable lpMode )
+ {
+ var mh$ = requireNonNull( GetConsoleMode$MH, "GetConsoleMode" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleHandle, lpMode );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int SetConsoleTitleW( Addressable lpConsoleTitle )
+ {
+ var mh$ = requireNonNull( SetConsoleTitleW$MH, "SetConsoleTitleW" );
+ try
+ {
+ return (int) mh$.invokeExact( lpConsoleTitle );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int SetConsoleCursorPosition( Addressable hConsoleOutput, COORD dwCursorPosition )
+ {
+ var mh$ = requireNonNull( SetConsoleCursorPosition$MH, "SetConsoleCursorPosition" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleOutput, dwCursorPosition.seg );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int FillConsoleOutputCharacterW( Addressable hConsoleOutput, char cCharacter, int nLength,
+ COORD dwWriteCoord, Addressable lpNumberOfCharsWritten )
+ {
+ var mh$ = requireNonNull( FillConsoleOutputCharacterW$MH, "FillConsoleOutputCharacterW" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleOutput, cCharacter, nLength, dwWriteCoord.seg, lpNumberOfCharsWritten );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int FillConsoleOutputAttribute( Addressable hConsoleOutput, short wAttribute, int nLength,
+ COORD dwWriteCoord, Addressable lpNumberOfAttrsWritten )
+ {
+ var mh$ = requireNonNull( FillConsoleOutputAttribute$MH, "FillConsoleOutputAttribute" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleOutput, wAttribute, nLength, dwWriteCoord.seg, lpNumberOfAttrsWritten );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int WriteConsoleW( Addressable hConsoleOutput, Addressable lpBuffer, int nNumberOfCharsToWrite,
+ Addressable lpNumberOfCharsWritten, Addressable lpReserved )
+ {
+ var mh$ = requireNonNull( WriteConsoleW$MH, "WriteConsoleW" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleOutput, lpBuffer, nNumberOfCharsToWrite, lpNumberOfCharsWritten,
+ lpReserved );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int ReadConsoleInputW( Addressable hConsoleInput, Addressable lpBuffer, int nLength,
+ Addressable lpNumberOfEventsRead )
+ {
+ var mh$ = requireNonNull( ReadConsoleInputW$MH, "ReadConsoleInputW" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int PeekConsoleInputW( Addressable hConsoleInput, Addressable lpBuffer, int nLength,
+ Addressable lpNumberOfEventsRead )
+ {
+ var mh$ = requireNonNull( PeekConsoleInputW$MH, "PeekConsoleInputW" );
+ try
+ {
+ return (int) mh$.invokeExact( hConsoleInput, lpBuffer, nLength, lpNumberOfEventsRead );
+ }
+ catch ( Throwable ex$ )
+ {
+ throw new AssertionError( "should not reach here", ex$ );
+ }
+ }
+
+ public static int GetConsoleScreenBufferInfo ( Addressable hConsoleOutput, CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo ) {
+ var mh$ = requireNonNull( GetConsoleScreenBufferInfo$MH, "GetConsoleScreenBufferInfo" );
+ try {
+ return (int)mh$.invokeExact(hConsoleOutput, lpConsoleScreenBufferInfo.seg);
+ } catch (Throwable ex$) {
+ throw new AssertionError("should not reach here", ex$);
+ }
+ }
+
+ public static int ScrollConsoleScreenBuffer ( Addressable hConsoleOutput, SMALL_RECT lpScrollRectangle,
+ SMALL_RECT lpClipRectangle, COORD dwDestinationOrigin,
+ CHAR_INFO lpFill) {
+ var mh$ = requireNonNull( ScrollConsoleScreenBuffer$MH, "ScrollConsoleScreenBuffer" );
+ try {
+ return (int)mh$.invokeExact(hConsoleOutput, lpScrollRectangle, lpClipRectangle, dwDestinationOrigin, lpFill);
+ } catch (Throwable ex$) {
+ throw new AssertionError("should not reach here", ex$);
+ }
+ }
+
+ public static int GetLastError (Object... x0) {
+ var mh$ = requireNonNull( GetLastError$MH, "GetLastError" );
+ try {
+ return (int)mh$.invokeExact(x0);
+ } catch (Throwable ex$) {
+ throw new AssertionError("should not reach here", ex$);
+ }
+ }
+
+
+ public static INPUT_RECORD[] readConsoleInputHelper( MemoryAddress handle, int count, boolean peek ) throws IOException
+ {
+ try ( MemorySession session = MemorySession.openImplicit() )
+ {
+ MemorySegment inputRecordPtr = session.allocateArray( INPUT_RECORD.LAYOUT, count );
+ MemorySegment length = session.allocate( JAVA_INT, 0 );
+ int res = peek ? PeekConsoleInputW( handle, inputRecordPtr, count, length )
+ : ReadConsoleInputW( handle, inputRecordPtr, count, length );
+ if ( res == 0 )
+ {
+ throw new IOException( "ReadConsoleInputW failed: " + getLastErrorMessage() );
+ }
+ int len = length.get( JAVA_INT, 0 );
+ return inputRecordPtr.elements( INPUT_RECORD.LAYOUT )
+ .map( INPUT_RECORD::new )
+ .limit( len )
+ .toArray( INPUT_RECORD[]::new );
+ }
+ }
+
+ public static String getLastErrorMessage()
+ {
+ int errorCode = GetLastError();
+ return getErrorMessage( errorCode );
+ }
+
+ public static String getErrorMessage( int errorCode )
+ {
+ int bufferSize = 160;
+ MemorySegment data = MemorySegment.allocateNative( bufferSize, MemorySession.openImplicit() );
+ FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, null, errorCode, 0, data, bufferSize, null );
+ return data.getUtf8String( 0 ).trim();
+ }
+
+ static final OfBoolean C_BOOL$LAYOUT = ValueLayout.JAVA_BOOLEAN;
+ static final OfByte C_CHAR$LAYOUT = ValueLayout.JAVA_BYTE;
+ static final OfChar C_WCHAR$LAYOUT = ValueLayout.JAVA_CHAR.withBitAlignment( 16 );
+ static final OfShort C_SHORT$LAYOUT = ValueLayout.JAVA_SHORT.withBitAlignment( 16 );
+ static final OfShort C_WORD$LAYOUT = ValueLayout.JAVA_SHORT.withBitAlignment( 16 );
+ static final OfInt C_DWORD$LAYOUT = ValueLayout.JAVA_INT.withBitAlignment( 32 );
+ static final OfInt C_INT$LAYOUT = JAVA_INT.withBitAlignment( 32 );
+ static final OfLong C_LONG$LAYOUT = ValueLayout.JAVA_LONG.withBitAlignment( 64 );
+ static final OfLong C_LONG_LONG$LAYOUT = ValueLayout.JAVA_LONG.withBitAlignment( 64 );
+ static final OfFloat C_FLOAT$LAYOUT = ValueLayout.JAVA_FLOAT.withBitAlignment( 32 );
+ static final OfDouble C_DOUBLE$LAYOUT = ValueLayout.JAVA_DOUBLE.withBitAlignment( 64 );
+ static final OfAddress C_POINTER$LAYOUT = ValueLayout.ADDRESS.withBitAlignment( 64 );
+
+ static final MethodHandle WaitForSingleObject$MH = downcallHandle(
+ "WaitForSingleObject",
+ FunctionDescriptor.of( C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT
+ )
+ );
+ static final MethodHandle GetStdHandle$MH = downcallHandle(
+ "GetStdHandle",
+ FunctionDescriptor.of( C_POINTER$LAYOUT,
+ C_INT$LAYOUT
+ )
+ );
+ static final MethodHandle FormatMessageW$MH = downcallHandle(
+ "FormatMessageW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle SetConsoleTextAttribute$MH = downcallHandle(
+ "SetConsoleTextAttribute",
+ FunctionDescriptor.of( C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_SHORT$LAYOUT
+ )
+ );
+ static final MethodHandle SetConsoleMode$MH = downcallHandle(
+ "SetConsoleMode",
+ FunctionDescriptor.of( C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT
+ )
+ );
+ static final MethodHandle GetConsoleMode$MH = downcallHandle(
+ "GetConsoleMode",
+ FunctionDescriptor.of( C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+
+ static final MethodHandle SetConsoleTitleW$MH = downcallHandle(
+ "SetConsoleTitleW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle SetConsoleCursorPosition$MH = downcallHandle(
+ "SetConsoleCursorPosition",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ COORD.LAYOUT
+ )
+ );
+ static final MethodHandle FillConsoleOutputCharacterW$MH = downcallHandle(
+ "FillConsoleOutputCharacterW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_SHORT$LAYOUT,
+ C_INT$LAYOUT,
+ COORD.LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle FillConsoleOutputAttribute$MH = downcallHandle(
+ "FillConsoleOutputAttribute",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_SHORT$LAYOUT,
+ C_INT$LAYOUT,
+ COORD.LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle WriteConsoleW$MH = downcallHandle(
+ "WriteConsoleW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+
+ static final MethodHandle ReadConsoleInputW$MH = downcallHandle(
+ "ReadConsoleInputW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle PeekConsoleInputW$MH = downcallHandle(
+ "PeekConsoleInputW",
+ FunctionDescriptor.of(
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_INT$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+
+ static final MethodHandle GetConsoleScreenBufferInfo$MH = downcallHandle(
+ "GetConsoleScreenBufferInfo",
+ FunctionDescriptor.of( C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+
+ static final MethodHandle ScrollConsoleScreenBuffer$MH = downcallHandle(
+ "ScrollConsoleScreenBuffer",
+ FunctionDescriptor.of(C_INT$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT,
+ C_POINTER$LAYOUT,
+ COORD.LAYOUT,
+ C_POINTER$LAYOUT
+ )
+ );
+ static final MethodHandle GetLastError$MH = downcallHandle(
+ "GetLastError",
+ FunctionDescriptor.of(C_INT$LAYOUT)
+ );
+
+
+ public static class INPUT_RECORD
+ {
+ static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
+ ValueLayout.JAVA_SHORT.withName( "EventType" ),
+ MemoryLayout.unionLayout(
+ KEY_EVENT_RECORD.LAYOUT.withName( "KeyEvent" ),
+ MOUSE_EVENT_RECORD.LAYOUT.withName( "MouseEvent" ),
+ WINDOW_BUFFER_SIZE_RECORD.LAYOUT.withName( "WindowBufferSizeEvent" ),
+ MENU_EVENT_RECORD.LAYOUT.withName( "MenuEvent" ),
+ FOCUS_EVENT_RECORD.LAYOUT.withName( "FocusEvent" )
+ ).withName( "Event" )
+ );
+ static final VarHandle EventType$VH = varHandle( LAYOUT, "EventType" );
+ static final long Event$OFFSET = byteOffset( LAYOUT, "Event" );
+
+ private final MemorySegment seg;
+
+ public INPUT_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ }
+
+ INPUT_RECORD( MemorySegment seg ) {
+ this.seg = seg;
+ }
+
+ public short eventType()
+ {
+ return (short) EventType$VH.get( seg );
+ }
+
+ public KEY_EVENT_RECORD keyEvent()
+ {
+ return new KEY_EVENT_RECORD( seg, Event$OFFSET );
+ }
+
+ public MOUSE_EVENT_RECORD mouseEvent()
+ {
+ return new MOUSE_EVENT_RECORD( seg, Event$OFFSET );
+ }
+
+ public FOCUS_EVENT_RECORD focusEvent()
+ {
+ return new FOCUS_EVENT_RECORD( seg, Event$OFFSET );
+ }
+
+ }
+
+ public static class MENU_EVENT_RECORD
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ C_DWORD$LAYOUT.withName( "dwCommandId" )
+ );
+ static final VarHandle COMMAND_ID = varHandle( LAYOUT, "dwCommandId" );
+
+ private final MemorySegment seg;
+
+ public MENU_EVENT_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT.byteSize(), MemorySession.openImplicit() ) );
+ }
+
+ MENU_EVENT_RECORD( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ public int commandId()
+ {
+ return (int) MENU_EVENT_RECORD.COMMAND_ID.get( seg );
+ }
+
+ public void commandId( int commandId )
+ {
+ MENU_EVENT_RECORD.COMMAND_ID.set( seg, commandId );
+ }
+
+ }
+
+ public static class FOCUS_EVENT_RECORD
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ C_BOOL$LAYOUT.withName( "bSetFocus" )
+ );
+ static final VarHandle SET_FOCUS = varHandle( LAYOUT, "bSetFocus" );
+
+ private final MemorySegment seg;
+
+ public FOCUS_EVENT_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT.byteSize(), MemorySession.openImplicit() ) );
+ }
+
+ FOCUS_EVENT_RECORD( MemorySegment seg )
+ {
+ this.seg = Objects.requireNonNull( seg );
+ }
+
+ FOCUS_EVENT_RECORD( MemorySegment seg, long offset )
+ {
+ this.seg = Objects.requireNonNull( seg ).asSlice( offset, LAYOUT.byteSize() );
+ }
+
+ public boolean setFocus()
+ {
+ return (boolean) FOCUS_EVENT_RECORD.SET_FOCUS.get( seg );
+ }
+
+ public void setFocus( boolean setFocus )
+ {
+ FOCUS_EVENT_RECORD.SET_FOCUS.set( seg, setFocus );
+ }
+
+ }
+
+ public static class WINDOW_BUFFER_SIZE_RECORD
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ COORD.LAYOUT.withName( "size" )
+ );
+ static final long SIZE_OFFSET = byteOffset( LAYOUT, "size" );
+
+ private final MemorySegment seg;
+
+ public WINDOW_BUFFER_SIZE_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT.byteSize(), MemorySession.openImplicit() ) );
+ }
+
+ WINDOW_BUFFER_SIZE_RECORD( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ public COORD size()
+ {
+ return new COORD( seg, SIZE_OFFSET );
+ }
+
+ public String toString()
+ {
+ return "WINDOW_BUFFER_SIZE_RECORD{size=" + this.size() + '}';
+ }
+
+ }
+
+ public static class MOUSE_EVENT_RECORD
+ {
+
+ private static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
+ COORD.LAYOUT.withName( "dwMousePosition" ),
+ C_DWORD$LAYOUT.withName( "dwButtonState" ),
+ C_DWORD$LAYOUT.withName( "dwControlKeyState" ),
+ C_DWORD$LAYOUT.withName( "dwEventFlags" )
+ );
+ private static final long MOUSE_POSITION_OFFSET = byteOffset( LAYOUT, "dwMousePosition" );
+ private static final VarHandle BUTTON_STATE = varHandle( LAYOUT, "dwButtonState" );
+ private static final VarHandle CONTROL_KEY_STATE = varHandle( LAYOUT, "dwControlKeyState" );
+ private static final VarHandle EVENT_FLAGS = varHandle( LAYOUT, "dwEventFlags" );
+
+ private final MemorySegment seg;
+
+ public MOUSE_EVENT_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ }
+
+ MOUSE_EVENT_RECORD( MemorySegment seg )
+ {
+ this.seg = Objects.requireNonNull( seg );
+ }
+
+ MOUSE_EVENT_RECORD( MemorySegment seg, long offset )
+ {
+ this.seg = Objects.requireNonNull( seg ).asSlice( offset, LAYOUT.byteSize() );
+ }
+
+ public COORD mousePosition()
+ {
+ return new COORD( seg, MOUSE_POSITION_OFFSET );
+ }
+
+ public int buttonState()
+ {
+ return (int) BUTTON_STATE.get( seg );
+ }
+
+ public int controlKeyState()
+ {
+ return (int) CONTROL_KEY_STATE.get( seg );
+ }
+
+ public int eventFlags()
+ {
+ return (int) EVENT_FLAGS.get( seg );
+ }
+
+ public String toString()
+ {
+ return "MOUSE_EVENT_RECORD{mousePosition=" + mousePosition() + ", buttonState=" + buttonState()
+ + ", controlKeyState=" + controlKeyState() + ", eventFlags=" + eventFlags() + '}';
+ }
+ }
+
+ public static class KEY_EVENT_RECORD
+ {
+
+ static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
+ JAVA_INT.withName( "bKeyDown" ),
+ ValueLayout.JAVA_SHORT.withName( "wRepeatCount" ),
+ ValueLayout.JAVA_SHORT.withName( "wVirtualKeyCode" ),
+ ValueLayout.JAVA_SHORT.withName( "wVirtualScanCode" ),
+ MemoryLayout.unionLayout(
+ ValueLayout.JAVA_CHAR.withName( "UnicodeChar" ),
+ ValueLayout.JAVA_BYTE.withName( "AsciiChar" )
+ ).withName( "uChar" ),
+ JAVA_INT.withName( "dwControlKeyState" )
+ );
+ static final VarHandle bKeyDown$VH = varHandle( LAYOUT, "bKeyDown" );
+ static final VarHandle wRepeatCount$VH = varHandle( LAYOUT, "wRepeatCount" );
+ static final VarHandle wVirtualKeyCode$VH = varHandle( LAYOUT, "wVirtualKeyCode" );
+ static final VarHandle wVirtualScanCode$VH = varHandle( LAYOUT, "wVirtualScanCode" );
+ static final VarHandle UnicodeChar$VH = varHandle( LAYOUT, "uChar", "UnicodeChar" );
+ static final VarHandle AsciiChar$VH = varHandle( LAYOUT, "uChar", "AsciiChar" );
+ static final VarHandle dwControlKeyState$VH = varHandle( LAYOUT, "dwControlKeyState" );
+
+ final MemorySegment seg;
+
+ public KEY_EVENT_RECORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ }
+
+ KEY_EVENT_RECORD( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ KEY_EVENT_RECORD( MemorySegment seg, long offset )
+ {
+ this.seg = Objects.requireNonNull( seg ).asSlice( offset, LAYOUT.byteSize() );
+ }
+
+ public boolean keyDown()
+ {
+ return (boolean) bKeyDown$VH.get( seg );
+ }
+
+ public int repeatCount()
+ {
+ return (int) wRepeatCount$VH.get( seg );
+ }
+
+ public short keyCode()
+ {
+ return (short) wVirtualKeyCode$VH.get( seg );
+ }
+
+ public short scanCode()
+ {
+ return (short) wVirtualScanCode$VH.get( seg );
+ }
+
+ public char uchar()
+ {
+ return (char) UnicodeChar$VH.get( seg );
+ }
+
+ public int controlKeyState()
+ {
+ return (int) dwControlKeyState$VH.get( seg );
+ }
+
+ public String toString()
+ {
+ return "KEY_EVENT_RECORD{keyDown=" + this.keyDown() + ", repeatCount=" + this.repeatCount() + ", keyCode="
+ + this.keyCode() + ", scanCode=" + this.scanCode() + ", uchar=" + this.uchar()
+ + ", controlKeyState="
+ + this.controlKeyState() + '}';
+ }
+ }
+
+ public static class CHAR_INFO
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ MemoryLayout.unionLayout(
+ C_WCHAR$LAYOUT.withName( "UnicodeChar" ),
+ C_CHAR$LAYOUT.withName( "AsciiChar" )
+ ).withName( "Char" ),
+ C_WORD$LAYOUT.withName( "Attributes" )
+ );
+ static final VarHandle UnicodeChar$VH = varHandle( LAYOUT, "Char", "UnicodeChar" );
+ static final VarHandle Attributes$VH = varHandle( LAYOUT, "Attributes" );
+
+ final MemorySegment seg;
+
+ public CHAR_INFO()
+ {
+ this( MemorySegment.allocateNative( LAYOUT.byteSize(), MemorySession.openImplicit() ) );
+ }
+
+ public CHAR_INFO( char c, short a )
+ {
+ this();
+ UnicodeChar$VH.set( seg, c );
+ Attributes$VH.set( seg, a );
+ }
+
+ CHAR_INFO( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ public char unicodeChar()
+ {
+ return (char) UnicodeChar$VH.get( seg );
+ }
+
+ }
+
+ public static class CONSOLE_SCREEN_BUFFER_INFO
+ {
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ COORD.LAYOUT.withName( "dwSize" ),
+ COORD.LAYOUT.withName( "dwCursorPosition" ),
+ C_WORD$LAYOUT.withName( "wAttributes" ),
+ SMALL_RECT.LAYOUT.withName( "srWindow" ),
+ COORD.LAYOUT.withName( "dwMaximumWindowSize" )
+ );
+ static final long dwSize$OFFSET = byteOffset( LAYOUT, "dwSize" );
+ static final long dwCursorPosition$OFFSET = byteOffset( LAYOUT, "dwCursorPosition" );
+ static final VarHandle wAttributes$VH = varHandle( LAYOUT, "wAttributes" );
+ static final long srWindow$OFFSET = byteOffset( LAYOUT, "srWindow" );
+
+ private final MemorySegment seg;
+
+ public CONSOLE_SCREEN_BUFFER_INFO()
+ {
+ this( MemorySegment.allocateNative( LAYOUT.byteSize(), MemorySession.openImplicit() ) );
+ }
+
+ CONSOLE_SCREEN_BUFFER_INFO( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ public COORD size()
+ {
+ return new COORD( seg, dwSize$OFFSET );
+ }
+
+ public COORD cursorPosition()
+ {
+ return new COORD( seg, dwCursorPosition$OFFSET );
+ }
+
+ public short attributes()
+ {
+ return (short) wAttributes$VH.get( seg );
+ }
+
+ public SMALL_RECT window()
+ {
+ return new SMALL_RECT( seg, srWindow$OFFSET );
+ }
+
+ public int windowWidth()
+ {
+ return this.window().width() + 1;
+ }
+
+ public int windowHeight()
+ {
+ return this.window().height() + 1;
+ }
+
+ public void attributes( short attr )
+ {
+ wAttributes$VH.set( seg, attr );
+ }
+ }
+
+ public static class COORD
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ C_SHORT$LAYOUT.withName( "x" ),
+ C_SHORT$LAYOUT.withName( "y" )
+ );
+ static final VarHandle x$VH = varHandle( LAYOUT, "x" );
+ static final VarHandle y$VH = varHandle( LAYOUT, "y" );
+
+ private final MemorySegment seg;
+
+ public COORD()
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ }
+
+ public COORD( short x, short y )
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ x( x );
+ y( y );
+ }
+
+ public COORD( COORD from )
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() )
+ .copyFrom( Objects.requireNonNull( from ).seg ) );
+ }
+
+ COORD( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ COORD( MemorySegment seg, long offset )
+ {
+ this.seg = Objects.requireNonNull( seg ).asSlice( offset, LAYOUT.byteSize() );
+ }
+
+ public short x()
+ {
+ return (short) COORD.x$VH.get( seg );
+ }
+
+ public void x( short x )
+ {
+ COORD.x$VH.set( seg, x );
+ }
+
+ public short y()
+ {
+ return (short) COORD.y$VH.get( seg );
+ }
+
+ public void y( short y )
+ {
+ COORD.y$VH.set( seg, y );
+ }
+
+ public COORD copy()
+ {
+ return new COORD( this );
+ }
+ }
+
+ public static class SMALL_RECT
+ {
+
+ static final GroupLayout LAYOUT = MemoryLayout.structLayout(
+ C_SHORT$LAYOUT.withName( "Left" ),
+ C_SHORT$LAYOUT.withName( "Top" ),
+ C_SHORT$LAYOUT.withName( "Right" ),
+ C_SHORT$LAYOUT.withName( "Bottom" )
+ );
+ static final VarHandle Left$VH = varHandle( LAYOUT, "Left" );
+ static final VarHandle Top$VH = varHandle( LAYOUT, "Top" );
+ static final VarHandle Right$VH = varHandle( LAYOUT, "Right" );
+ static final VarHandle Bottom$VH = varHandle( LAYOUT, "Bottom" );
+
+ private final MemorySegment seg;
+
+ public SMALL_RECT()
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() ) );
+ }
+
+ public SMALL_RECT( SMALL_RECT from )
+ {
+ this( MemorySegment.allocateNative( LAYOUT, MemorySession.openImplicit() )
+ .copyFrom( from.seg ) );
+ }
+
+ SMALL_RECT( MemorySegment seg, long offset )
+ {
+ this( seg.asSlice( offset, LAYOUT.byteSize() ) );
+ }
+
+ SMALL_RECT( MemorySegment seg )
+ {
+ this.seg = seg;
+ }
+
+ public short left()
+ {
+ return (short) Left$VH.get( seg );
+ }
+
+ public short top()
+ {
+ return (short) Top$VH.get( seg );
+ }
+
+ public short right()
+ {
+ return (short) Right$VH.get( seg );
+ }
+
+ public short bottom()
+ {
+ return (short) Bottom$VH.get( seg );
+ }
+
+ public short width()
+ {
+ return (short) ( this.right() - this.left() );
+ }
+
+ public short height()
+ {
+ return (short) ( this.bottom() - this.top() );
+ }
+
+ public void left( short l )
+ {
+ Left$VH.set( seg, l );
+ }
+
+ public void top( short t )
+ {
+ Top$VH.set( seg, t );
+ }
+
+ public SMALL_RECT copy()
+ {
+ return new SMALL_RECT( this );
+ }
+ }
+
+ private final static Linker LINKER = Linker.nativeLinker();
+
+ private final static SymbolLookup SYMBOL_LOOKUP;
+
+ static
+ {
+ SymbolLookup loaderLookup = SymbolLookup.loaderLookup();
+ SYMBOL_LOOKUP = name -> loaderLookup.lookup( name ).or( () -> LINKER.defaultLookup().lookup( name ) );
+ }
+
+ static MethodHandle downcallHandle( String name, FunctionDescriptor fdesc )
+ {
+ return SYMBOL_LOOKUP.lookup( name )
+ .map( addr -> LINKER.downcallHandle( addr, fdesc ) )
+ .orElse( null );
+ }
+
+ static T requireNonNull( T obj, String symbolName )
+ {
+ if ( obj == null )
+ {
+ throw new UnsatisfiedLinkError( "unresolved symbol: " + symbolName );
+ }
+ return obj;
+ }
+
+ static VarHandle varHandle( MemoryLayout layout, String e1 ) {
+ return layout.varHandle( MemoryLayout.PathElement.groupElement( e1 ) );
+ }
+
+ static VarHandle varHandle( MemoryLayout layout, String e1, String e2 ) {
+ return layout.varHandle( MemoryLayout.PathElement.groupElement( e1 ),
+ MemoryLayout.PathElement.groupElement( e2 ) );
+ }
+
+ static long byteOffset( MemoryLayout layout, String e1 ) {
+ return layout.byteOffset( MemoryLayout.PathElement.groupElement( e1 ) );
+ }
+
+}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/NativePty.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativePty.java
new file mode 100644
index 000000000..0958bcdae
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativePty.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.jline.terminal.impl.jep424;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+
+import org.jline.terminal.Attributes;
+import org.jline.terminal.Size;
+import org.jline.terminal.impl.AbstractPty;
+import org.jline.terminal.spi.TerminalProvider;
+
+class NativePty extends AbstractPty
+{
+ private final int master;
+ private final int slave;
+ private final int slaveOut;
+ private final String name;
+ private final FileDescriptor masterFD;
+ private final FileDescriptor slaveFD;
+ private final FileDescriptor slaveOutFD;
+
+ public NativePty( int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, String name )
+ {
+ this( master, masterFD, slave, slaveFD, slave, slaveFD, name );
+ }
+
+ public NativePty( int master, FileDescriptor masterFD, int slave, FileDescriptor slaveFD, int slaveOut,
+ FileDescriptor slaveOutFD, String name )
+ {
+ this.master = master;
+ this.slave = slave;
+ this.slaveOut = slaveOut;
+ this.name = name;
+ this.masterFD = masterFD;
+ this.slaveFD = slaveFD;
+ this.slaveOutFD = slaveOutFD;
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if ( master > 0 )
+ {
+ getMasterInput().close();
+ }
+ if ( slave > 0 )
+ {
+ getSlaveInput().close();
+ }
+ }
+
+ public int getMaster()
+ {
+ return master;
+ }
+
+ public int getSlave()
+ {
+ return slave;
+ }
+
+ public int getSlaveOut()
+ {
+ return slaveOut;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public FileDescriptor getMasterFD()
+ {
+ return masterFD;
+ }
+
+ public FileDescriptor getSlaveFD()
+ {
+ return slaveFD;
+ }
+
+ public FileDescriptor getSlaveOutFD()
+ {
+ return slaveOutFD;
+ }
+
+ public InputStream getMasterInput()
+ {
+ return new FileInputStream( getMasterFD() );
+ }
+
+ public OutputStream getMasterOutput()
+ {
+ return new FileOutputStream( getMasterFD() );
+ }
+
+ protected InputStream doGetSlaveInput()
+ {
+ return new FileInputStream( getSlaveFD() );
+ }
+
+ public OutputStream getSlaveOutput()
+ {
+ return new FileOutputStream( getSlaveOutFD() );
+ }
+
+ @Override
+ public Attributes getAttr() throws IOException
+ {
+ return CLibrary.getAttributes( slave );
+ }
+
+ @Override
+ protected void doSetAttr( Attributes attr ) throws IOException
+ {
+ CLibrary.setAttributes( slave, attr );
+ }
+
+ @Override
+ public Size getSize() throws IOException
+ {
+ return CLibrary.getTerminalSize( slave );
+ }
+
+ @Override
+ public void setSize( Size size ) throws IOException
+ {
+ CLibrary.setTerminalSize( slave, size );
+ }
+
+ @Override
+ public String toString()
+ {
+ return "NativePty[" + getName() + "]";
+ }
+
+ protected static FileDescriptor newDescriptor( int fd )
+ {
+ try
+ {
+ Constructor cns = FileDescriptor.class.getDeclaredConstructor( int.class );
+ cns.setAccessible( true );
+ return cns.newInstance( fd );
+ }
+ catch ( Throwable e )
+ {
+ throw new RuntimeException( "Unable to create FileDescriptor", e );
+ }
+ }
+
+ public static boolean isPosixSystemStream( TerminalProvider.Stream stream )
+ {
+ return switch ( stream )
+ {
+ case Input -> CLibrary.isTty( 0 );
+ case Output -> CLibrary.isTty( 1 );
+ case Error -> CLibrary.isTty( 2 );
+ };
+ }
+
+ public static String posixSystemStreamName( TerminalProvider.Stream stream )
+ {
+ return switch ( stream )
+ {
+ case Input -> CLibrary.ttyName( 0 );
+ case Output -> CLibrary.ttyName( 1 );
+ case Error -> CLibrary.ttyName( 2 );
+ };
+ }
+}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinConsoleWriter.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinConsoleWriter.java
new file mode 100644
index 000000000..f9ddb3395
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinConsoleWriter.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.jline.terminal.impl.jep424;
+
+import java.io.IOException;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.lang.foreign.ValueLayout;
+
+import org.jline.terminal.impl.AbstractWindowsConsoleWriter;
+
+import static org.jline.terminal.impl.jep424.Kernel32.GetStdHandle;
+import static org.jline.terminal.impl.jep424.Kernel32.STD_OUTPUT_HANDLE;
+import static org.jline.terminal.impl.jep424.Kernel32.WriteConsoleW;
+import static org.jline.terminal.impl.jep424.Kernel32.getLastErrorMessage;
+
+class NativeWinConsoleWriter extends AbstractWindowsConsoleWriter
+{
+
+ private final MemoryAddress console = GetStdHandle( STD_OUTPUT_HANDLE );
+
+ @Override
+ protected void writeConsole( char[] text, int len ) throws IOException
+ {
+ MemorySegment txt = MemorySegment.ofArray( text );
+ if ( WriteConsoleW( console, txt, len, null, null ) == 0 )
+ {
+ throw new IOException( "Failed to write to console: " + getLastErrorMessage() );
+ }
+ }
+}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinSysTerminal.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinSysTerminal.java
new file mode 100644
index 000000000..8a94110e7
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/NativeWinSysTerminal.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2022 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.jline.terminal.impl.jep424;
+
+import java.io.BufferedWriter;
+import java.io.IOError;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.nio.charset.Charset;
+import java.util.function.IntConsumer;
+
+import org.jline.terminal.Cursor;
+import org.jline.terminal.Size;
+import org.jline.terminal.impl.AbstractWindowsTerminal;
+import org.jline.terminal.spi.TerminalProvider;
+import org.jline.utils.OSUtils;
+
+import static java.lang.foreign.ValueLayout.JAVA_INT;
+import static org.jline.terminal.impl.jep424.Kernel32.*;
+import static org.jline.terminal.impl.jep424.Kernel32.GetConsoleMode;
+import static org.jline.terminal.impl.jep424.Kernel32.GetConsoleScreenBufferInfo;
+import static org.jline.terminal.impl.jep424.Kernel32.GetStdHandle;
+import static org.jline.terminal.impl.jep424.Kernel32.INPUT_RECORD;
+import static org.jline.terminal.impl.jep424.Kernel32.INVALID_HANDLE_VALUE;
+import static org.jline.terminal.impl.jep424.Kernel32.KEY_EVENT_RECORD;
+import static org.jline.terminal.impl.jep424.Kernel32.MOUSE_EVENT_RECORD;
+import static org.jline.terminal.impl.jep424.Kernel32.STD_ERROR_HANDLE;
+import static org.jline.terminal.impl.jep424.Kernel32.STD_INPUT_HANDLE;
+import static org.jline.terminal.impl.jep424.Kernel32.STD_OUTPUT_HANDLE;
+import static org.jline.terminal.impl.jep424.Kernel32.SetConsoleMode;
+import static org.jline.terminal.impl.jep424.Kernel32.WaitForSingleObject;
+import static org.jline.terminal.impl.jep424.Kernel32.getLastErrorMessage;
+import static org.jline.terminal.impl.jep424.Kernel32.readConsoleInputHelper;
+
+public class NativeWinSysTerminal extends AbstractWindowsTerminal
+{
+
+ public static NativeWinSysTerminal createTerminal( String name, String type, boolean ansiPassThrough,
+ Charset encoding,
+ boolean nativeSignals, SignalHandler signalHandler,
+ boolean paused,
+ TerminalProvider.Stream consoleStream ) throws IOException
+ {
+ Writer writer;
+ MemorySegment mode = MemorySegment.allocateNative( JAVA_INT, MemorySession.openImplicit() );
+ MemoryAddress consoleIn = GetStdHandle( STD_INPUT_HANDLE );
+ MemoryAddress console;
+ switch ( consoleStream )
+ {
+ case Output:
+ console = GetStdHandle( STD_OUTPUT_HANDLE );
+ break;
+ case Error:
+ console = GetStdHandle( STD_ERROR_HANDLE );
+ break;
+ default:
+ throw new IllegalArgumentException( "Unsupport stream for console: " + consoleStream );
+ }
+ if ( ansiPassThrough )
+ {
+ if ( type == null )
+ {
+ type = OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS;
+ }
+ writer = new NativeWinConsoleWriter();
+ }
+ else
+ {
+ if ( GetConsoleMode( console, mode ) == 0 )
+ {
+ throw new IOException( "Failed to get console mode: " + getLastErrorMessage() );
+ }
+ int m = mode.get( JAVA_INT, 0 );
+ if ( SetConsoleMode( console,
+ m | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING ) != 0 )
+ {
+ if ( type == null )
+ {
+ type = TYPE_WINDOWS_VTP;
+ }
+ writer = new NativeWinConsoleWriter();
+ }
+ else if ( OSUtils.IS_CONEMU )
+ {
+ if ( type == null )
+ {
+ type = TYPE_WINDOWS_CONEMU;
+ }
+ writer = new NativeWinConsoleWriter();
+ }
+ else
+ {
+ if ( type == null )
+ {
+ type = TYPE_WINDOWS;
+ }
+ writer = new WindowsAnsiWriter( new BufferedWriter( new NativeWinConsoleWriter() ) );
+ }
+ }
+ if ( GetConsoleMode( consoleIn, mode ) == 0 )
+ {
+ throw new IOException( "Failed to get console mode: " + getLastErrorMessage() );
+ }
+ NativeWinSysTerminal terminal = new NativeWinSysTerminal( writer, name, type, encoding, nativeSignals,
+ signalHandler, consoleIn, console );
+ // Start input pump thread
+ if ( !paused )
+ {
+ terminal.resume();
+ }
+ return terminal;
+ }
+
+ public static boolean isWindowsSystemStream( TerminalProvider.Stream stream )
+ {
+ MemoryAddress console;
+ MemorySegment mode = MemorySegment.allocateNative( JAVA_INT, MemorySession.openImplicit() );
+ switch ( stream )
+ {
+ case Input:
+ console = GetStdHandle( STD_INPUT_HANDLE );
+ break;
+ case Output:
+ console = GetStdHandle( STD_OUTPUT_HANDLE );
+ break;
+ case Error:
+ console = GetStdHandle( STD_ERROR_HANDLE );
+ break;
+ default:
+ return false;
+ }
+ return GetConsoleMode( console, mode ) != 0;
+ }
+
+ private final MemoryAddress console;
+ private final MemoryAddress outputHandle;
+
+ NativeWinSysTerminal( Writer writer, String name, String type, Charset encoding, boolean nativeSignals,
+ SignalHandler signalHandler,
+ MemoryAddress console, MemoryAddress outputHandle ) throws IOException
+ {
+ super( writer, name, type, encoding, nativeSignals, signalHandler );
+ this.console = console;
+ this.outputHandle = outputHandle;
+ }
+
+ @Override
+ protected int getConsoleMode()
+ {
+ try (MemorySession session = MemorySession.openImplicit() )
+ {
+ MemorySegment mode = session.allocate( JAVA_INT );
+ if ( GetConsoleMode( console, mode ) == 0 )
+ {
+ return -1;
+ }
+ return mode.get( JAVA_INT, 0 );
+ }
+ }
+
+ @Override
+ protected void setConsoleMode( int mode )
+ {
+ SetConsoleMode( console, mode );
+ }
+
+ public Size getSize()
+ {
+ CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
+ GetConsoleScreenBufferInfo( outputHandle, info );
+ return new Size( info.windowWidth(), info.windowHeight() );
+ }
+
+ @Override
+ public Size getBufferSize()
+ {
+ CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
+ GetConsoleScreenBufferInfo( outputHandle, info );
+ return new Size( info.size().x(), info.size().y() );
+ }
+
+ protected boolean processConsoleInput() throws IOException
+ {
+ INPUT_RECORD[] events;
+ if ( console != null && console.toRawLongValue() != INVALID_HANDLE_VALUE
+ && WaitForSingleObject( console, 100 ) == 0 )
+ {
+ events = readConsoleInputHelper( console, 1, false );
+ }
+ else
+ {
+ return false;
+ }
+
+ boolean flush = false;
+ for ( INPUT_RECORD event : events )
+ {
+ int eventType = event.eventType();
+ if ( eventType == KEY_EVENT )
+ {
+ KEY_EVENT_RECORD keyEvent = event.keyEvent();
+ processKeyEvent( keyEvent.keyDown(), keyEvent.keyCode(), keyEvent.uchar(), keyEvent.controlKeyState() );
+ flush = true;
+ }
+ else if ( eventType == WINDOW_BUFFER_SIZE_EVENT )
+ {
+ raise( Signal.WINCH );
+ }
+ else if ( eventType == MOUSE_EVENT )
+ {
+ processMouseEvent( event.mouseEvent() );
+ flush = true;
+ }
+ else if ( eventType == FOCUS_EVENT )
+ {
+ processFocusEvent( event.focusEvent().setFocus() );
+ }
+ }
+
+ return flush;
+ }
+
+ private final char[] focus = new char[] {'\033', '[', ' '};
+
+ private void processFocusEvent( boolean hasFocus ) throws IOException
+ {
+ if ( focusTracking )
+ {
+ focus[2] = hasFocus ? 'I' : 'O';
+ slaveInputPipe.write( focus );
+ }
+ }
+
+ private final char[] mouse = new char[] {'\033', '[', 'M', ' ', ' ', ' '};
+
+ private void processMouseEvent( MOUSE_EVENT_RECORD mouseEvent ) throws IOException
+ {
+ int dwEventFlags = mouseEvent.eventFlags();
+ int dwButtonState = mouseEvent.buttonState();
+ if ( tracking == MouseTracking.Off
+ || tracking == MouseTracking.Normal && dwEventFlags == MOUSE_MOVED
+ || tracking == MouseTracking.Button && dwEventFlags == MOUSE_MOVED
+ && dwButtonState == 0 )
+ {
+ return;
+ }
+ int cb = 0;
+ dwEventFlags &= ~DOUBLE_CLICK; // Treat double-clicks as normal
+ if ( dwEventFlags == MOUSE_WHEELED )
+ {
+ cb |= 64;
+ if ( ( dwButtonState >> 16 ) < 0 )
+ {
+ cb |= 1;
+ }
+ }
+ else if ( dwEventFlags == MOUSE_HWHEELED )
+ {
+ return;
+ }
+ else if ( ( dwButtonState & FROM_LEFT_1ST_BUTTON_PRESSED ) != 0 )
+ {
+ cb |= 0x00;
+ }
+ else if ( ( dwButtonState & RIGHTMOST_BUTTON_PRESSED ) != 0 )
+ {
+ cb |= 0x01;
+ }
+ else if ( ( dwButtonState & FROM_LEFT_2ND_BUTTON_PRESSED ) != 0 )
+ {
+ cb |= 0x02;
+ }
+ else
+ {
+ cb |= 0x03;
+ }
+ int cx = mouseEvent.mousePosition().x();
+ int cy = mouseEvent.mousePosition().y();
+ mouse[3] = (char) ( ' ' + cb );
+ mouse[4] = (char) ( ' ' + cx + 1 );
+ mouse[5] = (char) ( ' ' + cy + 1 );
+ slaveInputPipe.write( mouse );
+ }
+
+ @Override
+ public Cursor getCursorPosition( IntConsumer discarded )
+ {
+ CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
+ if ( GetConsoleScreenBufferInfo( outputHandle, info ) == 0 )
+ {
+ throw new IOError( new IOException( "Could not get the cursor position: " + getLastErrorMessage() ) );
+ }
+ return new Cursor( info.cursorPosition().x(), info.cursorPosition().y() );
+ }
+
+}
diff --git a/terminal/src/main/java/org/jline/terminal/impl/jep424/WindowsAnsiWriter.java b/terminal/src/main/java/org/jline/terminal/impl/jep424/WindowsAnsiWriter.java
new file mode 100644
index 000000000..80f0af9ee
--- /dev/null
+++ b/terminal/src/main/java/org/jline/terminal/impl/jep424/WindowsAnsiWriter.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2009-2018 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.jline.terminal.impl.jep424;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.foreign.MemoryAddress;
+import java.lang.foreign.MemorySegment;
+import java.lang.foreign.MemorySession;
+import java.lang.foreign.ValueLayout;
+
+import org.jline.utils.AnsiWriter;
+import org.jline.utils.Colors;
+
+import static org.jline.terminal.impl.jep424.Kernel32.BACKGROUND_BLUE;
+import static org.jline.terminal.impl.jep424.Kernel32.BACKGROUND_GREEN;
+import static org.jline.terminal.impl.jep424.Kernel32.BACKGROUND_INTENSITY;
+import static org.jline.terminal.impl.jep424.Kernel32.BACKGROUND_RED;
+import static org.jline.terminal.impl.jep424.Kernel32.CHAR_INFO;
+import static org.jline.terminal.impl.jep424.Kernel32.CONSOLE_SCREEN_BUFFER_INFO;
+import static org.jline.terminal.impl.jep424.Kernel32.COORD;
+import static org.jline.terminal.impl.jep424.Kernel32.FOREGROUND_BLUE;
+import static org.jline.terminal.impl.jep424.Kernel32.FOREGROUND_GREEN;
+import static org.jline.terminal.impl.jep424.Kernel32.FOREGROUND_INTENSITY;
+import static org.jline.terminal.impl.jep424.Kernel32.FOREGROUND_RED;
+import static org.jline.terminal.impl.jep424.Kernel32.FillConsoleOutputAttribute;
+import static org.jline.terminal.impl.jep424.Kernel32.FillConsoleOutputCharacterW;
+import static org.jline.terminal.impl.jep424.Kernel32.GetConsoleScreenBufferInfo;
+import static org.jline.terminal.impl.jep424.Kernel32.GetStdHandle;
+import static org.jline.terminal.impl.jep424.Kernel32.SMALL_RECT;
+import static org.jline.terminal.impl.jep424.Kernel32.STD_OUTPUT_HANDLE;
+import static org.jline.terminal.impl.jep424.Kernel32.ScrollConsoleScreenBuffer;
+import static org.jline.terminal.impl.jep424.Kernel32.SetConsoleCursorPosition;
+import static org.jline.terminal.impl.jep424.Kernel32.SetConsoleTextAttribute;
+import static org.jline.terminal.impl.jep424.Kernel32.SetConsoleTitleW;
+import static org.jline.terminal.impl.jep424.Kernel32.getLastErrorMessage;
+
+/**
+ * A Windows ANSI escape processor, that uses JNA to access native platform
+ * API's to change the console attributes.
+ *
+ * @since 1.0
+ * @author Hiram Chirino
+ * @author Joris Kuipers
+ */
+public final class WindowsAnsiWriter extends AnsiWriter {
+
+ private static final MemoryAddress console = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ private static final short FOREGROUND_BLACK = 0;
+ private static final short FOREGROUND_YELLOW = (short) (FOREGROUND_RED | FOREGROUND_GREEN);
+ private static final short FOREGROUND_MAGENTA = (short) (FOREGROUND_BLUE | FOREGROUND_RED);
+ private static final short FOREGROUND_CYAN = (short) (FOREGROUND_BLUE | FOREGROUND_GREEN);
+ private static final short FOREGROUND_WHITE = (short) (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+
+ private static final short BACKGROUND_BLACK = 0;
+ private static final short BACKGROUND_YELLOW = (short) (BACKGROUND_RED | BACKGROUND_GREEN);
+ private static final short BACKGROUND_MAGENTA = (short) (BACKGROUND_BLUE | BACKGROUND_RED);
+ private static final short BACKGROUND_CYAN = (short) (BACKGROUND_BLUE | BACKGROUND_GREEN);
+ private static final short BACKGROUND_WHITE = (short) (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
+
+ private static final short[] ANSI_FOREGROUND_COLOR_MAP = {
+ FOREGROUND_BLACK,
+ FOREGROUND_RED,
+ FOREGROUND_GREEN,
+ FOREGROUND_YELLOW,
+ FOREGROUND_BLUE,
+ FOREGROUND_MAGENTA,
+ FOREGROUND_CYAN,
+ FOREGROUND_WHITE,
+ };
+
+ private static final short[] ANSI_BACKGROUND_COLOR_MAP = {
+ BACKGROUND_BLACK,
+ BACKGROUND_RED,
+ BACKGROUND_GREEN,
+ BACKGROUND_YELLOW,
+ BACKGROUND_BLUE,
+ BACKGROUND_MAGENTA,
+ BACKGROUND_CYAN,
+ BACKGROUND_WHITE,
+ };
+
+ private final CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO();
+ private final short originalColors;
+
+ private boolean negative;
+ private boolean bold;
+ private boolean underline;
+ private short savedX = -1;
+ private short savedY = -1;
+
+ public WindowsAnsiWriter(Writer out) throws IOException {
+ super(out);
+ getConsoleInfo();
+ originalColors = info.attributes();
+ }
+
+ private void getConsoleInfo() throws IOException {
+ out.flush();
+ if (GetConsoleScreenBufferInfo(console, info) == 0) {
+ throw new IOException("Could not get the screen info: " + getLastErrorMessage());
+ }
+ if (negative) {
+ info.attributes( invertAttributeColors(info.attributes()) );
+ }
+ }
+
+ private void applyAttribute() throws IOException {
+ out.flush();
+ short attributes = info.attributes();
+ // bold is simulated by high foreground intensity
+ if (bold) {
+ attributes |= FOREGROUND_INTENSITY;
+ }
+ // underline is simulated by high foreground intensity
+ if (underline) {
+ attributes |= BACKGROUND_INTENSITY;
+ }
+ if (negative) {
+ attributes = invertAttributeColors(attributes);
+ }
+ if (SetConsoleTextAttribute(console, attributes) == 0) {
+ throw new IOException(getLastErrorMessage());
+ }
+ }
+
+ private short invertAttributeColors(short attributes) {
+ // Swap the the Foreground and Background bits.
+ int fg = 0x000F & attributes;
+ fg <<= 4;
+ int bg = 0X00F0 & attributes;
+ bg >>= 4;
+ attributes = (short) ((attributes & 0xFF00) | fg | bg);
+ return attributes;
+ }
+
+ private void applyCursorPosition() throws IOException {
+ info.cursorPosition().x( (short) Math.max(0, Math.min(info.size().x() - 1, info.cursorPosition().x())) );
+ info.cursorPosition().y( (short) Math.max(0, Math.min(info.size().y() - 1, info.cursorPosition().y())) );
+ if (SetConsoleCursorPosition(console, info.cursorPosition()) == 0) {
+ throw new IOException(getLastErrorMessage());
+ }
+ }
+
+ @Override
+ protected void processEraseScreen(int eraseOption) throws IOException {
+ getConsoleInfo();
+ try (MemorySession session = MemorySession.openImplicit()) {
+ MemorySegment written = session.allocate( ValueLayout.JAVA_INT );
+ switch (eraseOption) {
+ case ERASE_SCREEN:
+ COORD topLeft = new COORD( (short) 0, info.window().top() );
+ int screenLength = info.window().height() * info.size().x();
+ FillConsoleOutputAttribute(console, originalColors, screenLength, topLeft, written);
+ FillConsoleOutputCharacterW(console, ' ', screenLength, topLeft, written);
+ break;
+ case ERASE_SCREEN_TO_BEGINING:
+ COORD topLeft2 = new COORD( (short) 0, info.window().top() );
+ int lengthToCursor = (info.cursorPosition().y() - info.window().top()) * info.size().x()
+ + info.cursorPosition().x();
+ FillConsoleOutputAttribute(console, originalColors, lengthToCursor, topLeft2, written);
+ FillConsoleOutputCharacterW(console, ' ', lengthToCursor, topLeft2, written);
+ break;
+ case ERASE_SCREEN_TO_END:
+ int lengthToEnd = (info.window().bottom() - info.cursorPosition().y()) * info.size().x() +
+ (info.size().x() - info.cursorPosition().x());
+ FillConsoleOutputAttribute(console, originalColors, lengthToEnd, info.cursorPosition(), written);
+ FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition(), written);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void processEraseLine(int eraseOption) throws IOException {
+ getConsoleInfo();
+ try (MemorySession session = MemorySession.openImplicit()) {
+ MemorySegment written = session.allocate( ValueLayout.JAVA_INT );
+ switch (eraseOption) {
+ case ERASE_LINE:
+ COORD leftColCurrRow = new COORD( (short) 0, info.cursorPosition().y() );
+ FillConsoleOutputAttribute(console, originalColors, info.size().x(), leftColCurrRow, written);
+ FillConsoleOutputCharacterW(console, ' ', info.size().x(), leftColCurrRow, written);
+ break;
+ case ERASE_LINE_TO_BEGINING:
+ COORD leftColCurrRow2 = new COORD( (short) 0, info.cursorPosition().y() );
+ FillConsoleOutputAttribute(console, originalColors, info.cursorPosition().x(), leftColCurrRow2, written);
+ FillConsoleOutputCharacterW(console, ' ', info.cursorPosition().x(), leftColCurrRow2, written);
+ break;
+ case ERASE_LINE_TO_END:
+ int lengthToLastCol = info.size().x() - info.cursorPosition().x();
+ FillConsoleOutputAttribute(console, originalColors, lengthToLastCol, info.cursorPosition(), written);
+ FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition(), written);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ protected void processCursorUpLine(int count) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().x( (short) 0 );
+ info.cursorPosition().y( (short) (info.cursorPosition().y() - count) );
+ applyCursorPosition();
+ }
+
+ protected void processCursorDownLine(int count) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().x( (short) 0 );
+ info.cursorPosition().y( (short) (info.cursorPosition().y() + count) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processCursorLeft(int count) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().x( (short) (info.cursorPosition().x() - count) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processCursorRight(int count) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().x( (short) (info.cursorPosition().x() + count) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processCursorDown(int count) throws IOException {
+ getConsoleInfo();
+ int nb = Math.max(0, info.cursorPosition().y() + count - info.size().y() + 1);
+ if (nb != count) {
+ info.cursorPosition().y( (short) (info.cursorPosition().y() + count) );
+ applyCursorPosition();
+ }
+ if (nb > 0) {
+ SMALL_RECT scroll = new SMALL_RECT( info.window() );
+ scroll.top( (short) 0 );
+ COORD org = new COORD();
+ org.x( (short) 0 );
+ org.y( (short)(- nb) );
+ CHAR_INFO info = new CHAR_INFO( ' ', originalColors );
+ ScrollConsoleScreenBuffer(console, scroll, scroll, org, info);
+ }
+ }
+
+ @Override
+ protected void processCursorUp(int count) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().y( (short) (info.cursorPosition().y() - count) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processCursorTo(int row, int col) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().y( (short) (info.window().top() + row - 1) );
+ info.cursorPosition().x( (short) (col - 1) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processCursorToColumn(int x) throws IOException {
+ getConsoleInfo();
+ info.cursorPosition().x( (short) (x - 1) );
+ applyCursorPosition();
+ }
+
+ @Override
+ protected void processSetForegroundColorExt(int paletteIndex) throws IOException {
+ int color = Colors.roundColor(paletteIndex, 16);
+ info.attributes( (short) ((info.attributes() & ~0x0007) | ANSI_FOREGROUND_COLOR_MAP[color & 0x07]) );
+ info.attributes( (short) ((info.attributes() & ~FOREGROUND_INTENSITY) | (color >= 8 ? FOREGROUND_INTENSITY : 0)) );
+ applyAttribute();
+ }
+
+ @Override
+ protected void processSetBackgroundColorExt(int paletteIndex) throws IOException {
+ int color = Colors.roundColor(paletteIndex, 16);
+ info.attributes( (short) ((info.attributes() & ~0x0070) | ANSI_BACKGROUND_COLOR_MAP[color & 0x07]) );
+ info.attributes( (short) ((info.attributes() & ~BACKGROUND_INTENSITY) | (color >= 8 ? BACKGROUND_INTENSITY : 0)) );
+ applyAttribute();
+ }
+
+ @Override
+ protected void processDefaultTextColor() throws IOException {
+ info.attributes( (short) ((info.attributes() & ~0x000F) | (originalColors & 0xF)) );
+ info.attributes( (short) (info.attributes() & ~FOREGROUND_INTENSITY) );
+ applyAttribute();
+ }
+
+ @Override
+ protected void processDefaultBackgroundColor() throws IOException {
+ info.attributes( (short) ((info.attributes() & ~0x00F0) | (originalColors & 0xF0)) );
+ info.attributes( (short) (info.attributes() & ~BACKGROUND_INTENSITY) );
+ applyAttribute();
+ }
+
+ @Override
+ protected void processAttributeRest() throws IOException {
+ info.attributes( (short) ((info.attributes() & ~0x00FF) | originalColors) );
+ this.negative = false;
+ this.bold = false;
+ this.underline = false;
+ applyAttribute();
+ }
+
+ @Override
+ protected void processSetAttribute(int attribute) throws IOException {
+ switch (attribute) {
+ case ATTRIBUTE_INTENSITY_BOLD:
+ bold = true;
+ applyAttribute();
+ break;
+ case ATTRIBUTE_INTENSITY_NORMAL:
+ bold = false;
+ applyAttribute();
+ break;
+
+ case ATTRIBUTE_UNDERLINE:
+ underline = true;
+ applyAttribute();
+ break;
+ case ATTRIBUTE_UNDERLINE_OFF:
+ underline = false;
+ applyAttribute();
+ break;
+
+ case ATTRIBUTE_NEGATIVE_ON:
+ negative = true;
+ applyAttribute();
+ break;
+ case ATTRIBUTE_NEGATIVE_OFF:
+ negative = false;
+ applyAttribute();
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected void processSaveCursorPosition() throws IOException {
+ getConsoleInfo();
+ savedX = info.cursorPosition().x();
+ savedY = info.cursorPosition().y();
+ }
+
+ @Override
+ protected void processRestoreCursorPosition() throws IOException {
+ // restore only if there was a save operation first
+ if (savedX != -1 && savedY != -1) {
+ out.flush();
+ info.cursorPosition().x( savedX );
+ info.cursorPosition().y( savedY );
+ applyCursorPosition();
+ }
+ }
+
+ @Override
+ protected void processInsertLine(int optionInt) throws IOException {
+ getConsoleInfo();
+ SMALL_RECT scroll = info.window().copy();
+ scroll.top( info.cursorPosition().y() );
+ COORD org = new COORD( (short) 0, (short)(info.cursorPosition().y() + optionInt) );
+ CHAR_INFO info = new CHAR_INFO( ' ', originalColors );
+ if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) {
+ throw new IOException(getLastErrorMessage());
+ }
+ }
+
+ @Override
+ protected void processDeleteLine(int optionInt) throws IOException {
+ getConsoleInfo();
+ SMALL_RECT scroll = info.window().copy();
+ scroll.top( info.cursorPosition().y() );
+ COORD org = new COORD( (short) 0, (short)(info.cursorPosition().y() - optionInt) );
+ CHAR_INFO info = new CHAR_INFO( ' ', originalColors );
+ if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) {
+ throw new IOException(getLastErrorMessage());
+ }
+ }
+
+ @Override
+ protected void processChangeWindowTitle(String title) {
+ try ( MemorySession session = MemorySession.openImplicit() )
+ {
+ MemorySegment str = session.allocateUtf8String( title );
+ SetConsoleTitleW( str );
+ }
+ }
+}