diff --git a/README.md b/README.md index 79d04cad5..0e053b456 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,10 @@ The dependencies are minimal: you may use JLine without any dependency on *nix s You can also use fine grained jars: * `jline-terminal`: the `Terminal` api and implementations * `jline-terminal-jansi`: terminal implementations leveraging the `Jansi` library +* `jline-terminal-jni`: terminal implementations leveraging the JNI native library * `jline-terminal-jna`: terminal implementations leveraging the `JNA` library +* `jline-terminal-ffm`: terminal implementations leveraging the Foreign Functions & Mapping layer +* `jline-native`: the native library * `jline-reader`: the line reader (including completion, history, etc...) * `jline-style`: styling api * `jline-remote-ssh`: helpers for using jline with [Mina SSHD](http://mina.apache.org/sshd-project/) diff --git a/demo/jline-repl.bat b/demo/jline-repl.bat index c68fdbd03..f7a5a2bed 100644 --- a/demo/jline-repl.bat +++ b/demo/jline-repl.bat @@ -1,75 +1,75 @@ -@echo off - -set DIRNAME=%~dp0% -set ROOTDIR=%DIRNAME%\.. -set TARGETDIR=%DIRNAME%target - -rem initialization -if not exist %TARGETDIR%\lib ( - echo Build jline with maven before running the demo - goto END -) - -goto :SETUP_CLASSPATH - -: APPEND_TO_CLASSPATH -set filename=%~1 -set cp=%cp%;%TARGETDIR%\lib\%filename% -goto :EOF - -:SETUP_CLASSPATH -set cp=%TARGETDIR%\classes -rem JLINE -pushd %TARGETDIR%\lib -for %%G in (jline-*.jar) do call:APPEND_TO_CLASSPATH %%G -rem Groovy -for %%G in (groovy-*.jar) do call:APPEND_TO_CLASSPATH %%G -for %%G in (ivy-*.jar) do call:APPEND_TO_CLASSPATH %%G - -set "opts=%JLINE_OPTS%" -set "logconf=%DIRNAME%etc\logging.properties" -:RUN_LOOP - if "%1" == "jansi" goto :EXECUTE_JANSI - if "%1" == "jna" goto :EXECUTE_JNA - if "%1" == "debug" goto :EXECUTE_DEBUG - if "%1" == "debugs" goto :EXECUTE_DEBUGS - if "%1" == "verbose" goto :EXECUTE_VERBOSE - if "%1" == "" goto :EXECUTE_MAIN - set "opts=%opts% %~1" - shift - goto :RUN_LOOP - -:EXECUTE_JANSI - for %%G in (jansi-*.jar) do call:APPEND_TO_CLASSPATH %%G - shift - goto :RUN_LOOP - -:EXECUTE_JNA - for %%G in (jna-*.jar) do call:APPEND_TO_CLASSPATH %%G - shift - goto :RUN_LOOP - -:EXECUTE_DEBUG - set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" - shift - goto :RUN_LOOP - -:EXECUTE_DEBUGS - set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" - shift - goto :RUN_LOOP - -:EXECUTE_VERBOSE - set "logconf=%DIRNAME%etc\logging-verbose.properties" - shift - goto :RUN_LOOP - -:EXECUTE_MAIN -popd - -rem Launching Groovy REPL... -echo Launching Groovy REPL... -echo Classpath: %cp% -java -cp %cp% %opts% -Dgosh.home=%DIRNAME% -Djava.util.logging.config.file=%logconf% org.jline.demo.Repl - +@echo off + +set DIRNAME=%~dp0% +set ROOTDIR=%DIRNAME%\.. +set TARGETDIR=%DIRNAME%target + +rem initialization +if not exist %TARGETDIR%\lib ( + echo Build jline with maven before running the demo + goto END +) + +goto :SETUP_CLASSPATH + +: APPEND_TO_CLASSPATH +set filename=%~1 +set cp=%cp%;%TARGETDIR%\lib\%filename% +goto :EOF + +:SETUP_CLASSPATH +set cp=%TARGETDIR%\classes +rem JLINE +pushd %TARGETDIR%\lib +for %%G in (jline-*.jar) do call:APPEND_TO_CLASSPATH %%G +rem Groovy +for %%G in (groovy-*.jar) do call:APPEND_TO_CLASSPATH %%G +for %%G in (ivy-*.jar) do call:APPEND_TO_CLASSPATH %%G + +set "opts=%JLINE_OPTS%" +set "logconf=%DIRNAME%etc\logging.properties" +:RUN_LOOP + if "%1" == "jansi" goto :EXECUTE_JANSI + if "%1" == "jna" goto :EXECUTE_JNA + if "%1" == "debug" goto :EXECUTE_DEBUG + if "%1" == "debugs" goto :EXECUTE_DEBUGS + if "%1" == "verbose" goto :EXECUTE_VERBOSE + if "%1" == "" goto :EXECUTE_MAIN + set "opts=%opts% %~1" + shift + goto :RUN_LOOP + +:EXECUTE_JANSI + for %%G in (jansi-*.jar) do call:APPEND_TO_CLASSPATH %%G + shift + goto :RUN_LOOP + +:EXECUTE_JNA + for %%G in (jna-*.jar) do call:APPEND_TO_CLASSPATH %%G + shift + goto :RUN_LOOP + +:EXECUTE_DEBUG + set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005" + shift + goto :RUN_LOOP + +:EXECUTE_DEBUGS + set "opts=%opts% -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" + shift + goto :RUN_LOOP + +:EXECUTE_VERBOSE + set "logconf=%DIRNAME%etc\logging-verbose.properties" + shift + goto :RUN_LOOP + +:EXECUTE_MAIN +popd + +rem Launching Groovy REPL... +echo Launching Groovy REPL... +echo Classpath: %cp% +java -cp %cp% %opts% -Dgosh.home=%DIRNAME% -Djava.util.logging.config.file=%logconf% org.jline.demo.Repl + :END \ No newline at end of file diff --git a/demo/pom.xml b/demo/pom.xml index 9c7ca7eea..8eb6eb17a 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -31,6 +31,10 @@ org.jline jline-terminal-ffm + + org.jline + jline-terminal-jni + org.jline jline-terminal-jansi diff --git a/jline/pom.xml b/jline/pom.xml index c10bd7fa8..ef47dae02 100644 --- a/jline/pom.xml +++ b/jline/pom.xml @@ -68,6 +68,11 @@ jline-terminal test + + org.jline + jline-terminal-jni + test + org.jline jline-terminal-jansi @@ -139,6 +144,14 @@ false ${project.build.directory}/generated-sources + + org.jline + jline-terminal-jni + sources + jar + false + ${project.build.directory}/generated-sources + org.jline jline-terminal-jansi @@ -213,6 +226,14 @@ ${project.build.directory}/generated-resources **/*.class + + org.jline + jline-terminal-jni + jar + false + ${project.build.directory}/generated-resources + **/*.class + org.jline jline-terminal-jansi diff --git a/native/Makefile b/native/Makefile index 08b450541..4b42b1bbc 100644 --- a/native/Makefile +++ b/native/Makefile @@ -23,6 +23,7 @@ linux-armv6-digest:=@sha256:7bad6ab302af34bdf6634c8c2b02c8dc6ac932c67da9ecc199c5 linux-x86-digest:=:latest windows-static-x86-digest:=@sha256:99d7069789560ef77a7504ead4a2b5e3c245cb45a45f964c74fecbf649398d3a windows-static-x64-digest:=@sha256:f159861bc80b29e5dafb223477167bec53ecec6cdacb051d31e90c5823542100 +windows-arm64-digest:=@sha256:f4b3c1a49ec8b53418cef1499dc3f9a54a5570b7a3ecdf42fc8c83eb94b01b7d cross-build-digest:=@sha256:8dbaa86462270db93ae1b1b319bdd88d89272faf3a68632daf4fa36b414a326e freebsd-crossbuild-digest:=@sha256:cda62697a15d8bdc0bc26e780b1771ee78f12c55e7d5813e62c478af5a747c43 mcandre-snek-digest:=@sha256:9f84e9fcdf66daafc1f1c3fb772a6c97977714e17800aeac2e3bbe5dc5039dd0 @@ -68,9 +69,9 @@ $(JLINENATIVE_OUT)/%.o: src/main/native/%.c ifeq ($(OS_NAME), Windows) $(JLINENATIVE_OUT)/$(LIBNAME): ducible endif -$(JLINENATIVE_OUT)/$(LIBNAME): $(JLINENATIVE_OUT)/jlinenative.o +$(JLINENATIVE_OUT)/$(LIBNAME): $(JLINENATIVE_OUT)/jlinenative.o $(JLINENATIVE_OUT)/clibrary.o $(JLINENATIVE_OUT)/kernel32.o @mkdir -p $(@D) - $(CC) $(CCFLAGS) -o $@ $(JLINENATIVE_OUT)/jlinenative.o $(LINKFLAGS) + $(CC) $(CCFLAGS) -o $@ $(JLINENATIVE_OUT)/jlinenative.o $(JLINENATIVE_OUT)/clibrary.o $(JLINENATIVE_OUT)/kernel32.o $(LINKFLAGS) ifeq ($(OS_NAME), Windows) target/ducible/ducible $(JLINENATIVE_OUT)/$(LIBNAME) endif @@ -81,8 +82,11 @@ NATIVE_DLL:=$(NATIVE_DIR)/$(LIBNAME) # For cross-compilation, install docker. See also https://github.com/dockcross/dockcross # Disabled linux-armv6 build because of this issue; https://github.com/dockcross/dockcross/issues/190 -native-all: linux-x86 linux-x86_64 linux-arm linux-armv6 linux-armv7 \ - linux-arm64 linux-ppc64 win-x86 win-x86_64 mac-x86 mac-x86_64 mac-arm64 freebsd-x86 freebsd-x86_64 +native-all: \ + linux-x86 linux-x86_64 linux-arm linux-armv6 linux-armv7 linux-arm64 linux-ppc64 linux-riscv64 \ + win-x86 win-x86_64 win-arm64 \ + mac-x86 mac-x86_64 mac-arm64 \ + freebsd-x86 freebsd-x86_64 native: $(NATIVE_DLL) @@ -139,6 +143,12 @@ target/dockcross/dockcross-windows-static-x64: dockcross win-x86_64: download-includes target/dockcross/dockcross-windows-static-x64 target/dockcross/dockcross-windows-static-x64 bash -c 'make clean-native native CROSS_PREFIX=x86_64-w64-mingw32.static- OS_NAME=Windows OS_ARCH=x86_64' +target/dockcross/dockcross-windows-arm64: dockcross + docker run --rm dockcross/windows-arm64$(windows-arm64-digest) > target/dockcross/dockcross-windows-arm64 + chmod +x target/dockcross/dockcross-windows-arm64 +win-arm64: download-includes target/dockcross/dockcross-windows-arm64 + target/dockcross/dockcross-windows-arm64 bash -c 'make clean-native native CROSS_PREFIX=aarch64-w64-mingw32- OS_NAME=Windows OS_ARCH=arm64' + mac-x86: download-includes docker run -it --rm -v $$PWD:/workdir --user $$(id -u):$$(id -g) \ -e CROSS_TRIPLE=i386-apple-darwin multiarch/crossbuild$(cross-build-digest) make clean-native native OS_NAME=Mac OS_ARCH=x86 diff --git a/native/Makefile.common b/native/Makefile.common index fdc17ff86..9c2fc01e3 100644 --- a/native/Makefile.common +++ b/native/Makefile.common @@ -17,7 +17,7 @@ # os=Default is meant to be generic unix/linux -known_targets := Linux-x86 Linux-x86_64 Linux-arm Linux-armv6 Linux-armv7 Linux-android-arm Linux-ppc64 Mac-x86 Mac-x86_64 Mac-arm64 DragonFly-x86_64 FreeBSD-x86_64 OpenBSD-x86_64 Windows-x86 Windows-x86_64 SunOS-sparcv9 HPUX-ia64_32 +known_targets := Linux-x86 Linux-x86_64 Linux-arm Linux-armv6 Linux-armv7 Linux-android-arm Linux-arm64 Linux-ppc64 Linux-riscv64 Mac-x86 Mac-x86_64 Mac-arm64 DragonFly-x86_64 FreeBSD-x86_64 OpenBSD-x86_64 Windows-x86 Windows-x86_64 SunOS-sparcv9 HPUX-ia64_32 target := $(OS_NAME)-$(OS_ARCH) ifeq (,$(findstring $(strip $(target)),$(known_targets))) @@ -32,105 +32,96 @@ Default_STRIP := $(CROSS_PREFIX)strip Default_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -fvisibility=hidden Default_LINKFLAGS := -shared Default_LIBNAME := libjlinenative.so -Default_JANSI_FLAGS := Linux-x86_CC := $(CROSS_PREFIX)gcc Linux-x86_STRIP := $(CROSS_PREFIX)strip Linux-x86_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -m32 -fvisibility=hidden Linux-x86_LINKFLAGS := -shared -static-libgcc Linux-x86_LIBNAME := libjlinenative.so -Linux-x86_JANSI_FLAGS := Linux-x86_64_CC := $(CROSS_PREFIX)gcc Linux-x86_64_STRIP := $(CROSS_PREFIX)strip Linux-x86_64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -m64 -fvisibility=hidden Linux-x86_64_LINKFLAGS := -shared -static-libgcc Linux-x86_64_LIBNAME := libjlinenative.so -Linux-x86_64_JANSI_FLAGS := Linux-arm_CC := $(CROSS_PREFIX)gcc Linux-arm_STRIP := $(CROSS_PREFIX)strip Linux-arm_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -mfloat-abi=softfp -mfpu=vfp -fvisibility=hidden Linux-arm_LINKFLAGS := -shared -static-libgcc Linux-arm_LIBNAME := libjlinenative.so -Linux-arm_JANSI_FLAGS := Linux-armv6_CC := $(CROSS_PREFIX)gcc Linux-armv6_STRIP := $(CROSS_PREFIX)strip Linux-armv6_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -mfloat-abi=hard -mfpu=vfp -fPIC -fvisibility=hidden Linux-armv6_LINKFLAGS := -shared -static-libgcc Linux-armv6_LIBNAME := libjlinenative.so -Linux-armv6_JANSI_FLAGS := Linux-armv7_CC := $(CROSS_PREFIX)gcc Linux-armv7_STRIP := $(CROSS_PREFIX)strip Linux-armv7_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -mfloat-abi=hard -mfpu=vfp -fPIC -fvisibility=hidden Linux-armv7_LINKFLAGS := -shared -static-libgcc Linux-armv7_LIBNAME := libjlinenative.so -Linux-armv7_JANSI_FLAGS := Linux-arm64_CC := $(CROSS_PREFIX)gcc Linux-arm64_STRIP := $(CROSS_PREFIX)strip -Linux-arm64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -mfloat-abi=hard -mfpu=vfp -fPIC -fvisibility=hidden +Linux-arm64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -march=armv8-a -fPIC -fvisibility=hidden Linux-arm64_LINKFLAGS := -shared -static-libgcc Linux-arm64_LIBNAME := libjlinenative.so -Linux-arm64_JANSI_FLAGS := Linux-ppc64_CC := $(CROSS_PREFIX)gcc Linux-ppc64_STRIP := $(CROSS_PREFIX)strip Linux-ppc64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -fvisibility=hidden Linux-ppc64_LINKFLAGS := -shared -static-libgcc Linux-ppc64_LIBNAME := libjlinenative.so -Linux-ppc64_JANSI_FLAGS := + +Linux-riscv64_CC := $(CROSS_PREFIX)gcc +Linux-riscv64_STRIP := $(CROSS_PREFIX)strip +Linux-riscv64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -march=rv64g -fPIC -fvisibility=hidden +Linux-riscv64_LINKFLAGS := -shared -static-libgcc +Linux-riscv64_LIBNAME := libjlinenative.so DragonFly-x86_64_CC := $(CROSS_PREFIX)cc DragonFly-x86_64_STRIP := $(CROSS_PREFIX)strip DragonFly-x86_64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -O2 -fPIC -fvisibility=hidden DragonFly-x86_64_LINKFLAGS := -shared DragonFly-x86_64_LIBNAME := libjlinenative.so -DragonFly-x86_64_JANSI_FLAGS := FreeBSD-x86_CC := $(CROSS_PREFIX)gcc FreeBSD-x86_STRIP := $(CROSS_PREFIX)strip FreeBSD-x86_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -fvisibility=hidden FreeBSD-x86_LINKFLAGS := -shared FreeBSD-x86_LIBNAME := libjlinenative.so -FreeBSD-x86_JANSI_FLAGS := FreeBSD-x86_64_CC := $(CROSS_PREFIX)gcc FreeBSD-x86_64_STRIP := $(CROSS_PREFIX)strip FreeBSD-x86_64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -fvisibility=hidden FreeBSD-x86_64_LINKFLAGS := -shared FreeBSD-x86_64_LIBNAME := libjlinenative.so -FreeBSD-x86_64_JANSI_FLAGS := OpenBSD-x86_64_CC := $(CROSS_PREFIX)gcc OpenBSD-x86_64_STRIP := $(CROSS_PREFIX)strip OpenBSD-x86_64_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -fvisibility=hidden OpenBSD-x86_64_LINKFLAGS := -shared OpenBSD-x86_64_LIBNAME := libjlinenative.so -OpenBSD-x86_64_JANSI_FLAGS := SunOS-sparcv9_CC := $(CROSS_PREFIX)gcc SunOS-sparcv9_STRIP := $(CROSS_PREFIX)strip SunOS-sparcv9_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -O2s-fPIC -m64 -fvisibility=hidden SunOS-sparcv9_LINKFLAGS := -shared -static-libgcc SunOS-sparcv9_LIBNAME := libjlinenative.so -SunOS-sparcv9_JANSI_FLAGS := HPUX-ia64_32_CC := cc HPUX-ia64_32_STRIP := strip HPUX-ia64_32_CCFLAGS := -Itarget/inc -Itarget/inc/unix +Osize +z -Bhidden HPUX-ia64_32_LINKFLAGS := -b -HPUX-ia64_32_LIBNAME := libjlinenative.so -HPUX-ia64_32_JANSI_FLAGS := +SHPUX-ia64_32_LIBNAME := libjlinenative.so Mac-x86_CC := gcc Mac-x86_STRIP := strip -x Mac-x86_CCFLAGS := -I$(JAVA_HOME)/include -Itarget/inc -Itarget/inc/unix -Os -fPIC -mmacosx-version-min=10.4 -fvisibility=hidden Mac-x86_LINKFLAGS := -dynamiclib Mac-x86_LIBNAME := libjlinenative.jnilib -Mac-x86_JANSI_FLAGS := -DJANSI_ENABLE_LOCKING_STYLE=0 Mac-x86_64_CC := gcc -arch $(OS_ARCH) Mac-x86_64_STRIP := strip -x @@ -141,7 +132,6 @@ endif Mac-x86_64_CCFLAGS := -I$(MAC_SDK)/System/Library/Frameworks/JavaVM.framework/Headers -Itarget/inc -Itarget/inc/unix -Os -fPIC -mmacosx-version-min=10.6 -fvisibility=hidden Mac-x86_64_LINKFLAGS := -dynamiclib Mac-x86_64_LIBNAME := libjlinenative.jnilib -Mac-x86_64_JANSI_FLAGS := Mac-arm64_CC := $(CROSS_PREFIX)clang -v Mac-arm64_STRIP := $(CROSS_PREFIX)strip -x @@ -149,21 +139,18 @@ MAC_SDK := /usr/local/osxcross/SDK/MacOSX11.3.sdk/ Mac-arm64_CCFLAGS := -I$(MAC_SDK)/System/Library/Frameworks/JavaVM.framework/Headers -Itarget/inc -Itarget/inc/unix -Os -fPIC -mmacosx-version-min=11.0 -fvisibility=hidden Mac-arm64_LINKFLAGS := -shared Mac-arm64_LIBNAME := libjlinenative.jnilib -Mac-arm64_JANSI_FLAGS := Windows-x86_CC := $(CROSS_PREFIX)gcc Windows-x86_STRIP := $(CROSS_PREFIX)strip Windows-x86_CCFLAGS := -D_JNI_IMPLEMENTATION_ -Itarget/inc -Itarget/inc/windows -Os Windows-x86_LINKFLAGS := -Wl,--kill-at -shared -static-libgcc Windows-x86_LIBNAME := jlinenative.dll -Windows-x86_JANSI_FLAGS := Windows-x86_64_CC := $(CROSS_PREFIX)gcc Windows-x86_64_STRIP := $(CROSS_PREFIX)strip Windows-x86_64_CCFLAGS := -D_JNI_IMPLEMENTATION_ -Itarget/inc -Itarget/inc/windows -Os Windows-x86_64_LINKFLAGS := -Wl,--kill-at -shared -static-libgcc Windows-x86_64_LIBNAME := jlinenative.dll -Windows-x86_64_JANSI_FLAGS := CC := $($(target)_CC) diff --git a/native/src/main/java/org/jline/nativ/CLibrary.java b/native/src/main/java/org/jline/nativ/CLibrary.java new file mode 100644 index 000000000..bc69f59d5 --- /dev/null +++ b/native/src/main/java/org/jline/nativ/CLibrary.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2009-2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.nativ; + +/** + * Interface to access some low level POSIX functions,. + * + * @see JLineNativeLoader + */ +@SuppressWarnings("unused") +public class CLibrary { + + // + // Initialization + // + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + // + // Constants + // + + public static int TCSANOW; + public static int TCSADRAIN; + public static int TCSAFLUSH; + public static long TIOCGWINSZ; + public static long TIOCSWINSZ; + + /** + * test whether a file descriptor refers to a terminal + * + * @param fd file descriptor + * @return isatty() returns 1 if fd is an open file descriptor referring to a + * terminal; otherwise 0 is returned, and errno is set to indicate the + * error + * @see ISATTY(3) man-page + * @see ISATTY(3P) man-page + */ + public static native int isatty(int fd); + + public static native String ttyname(int filedes); + + /** + * The openpty() function finds an available pseudoterminal and returns + * file descriptors for the master and slave in amaster and aslave. + * + * @param amaster master return value + * @param aslave slave return value + * @param name filename return value + * @param termios optional pty attributes + * @param winsize optional size + * @return 0 on success + * @see OPENPTY(3) man-page + */ + public static native int openpty(int[] amaster, int[] aslave, byte[] name, Termios termios, WinSize winsize); + + public static native int tcgetattr(int filedes, Termios termios); + + public static native int tcsetattr(int filedes, int optional_actions, Termios termios); + + /** + * Control a STREAMS device. + * + * @see IOCTL(3P) man-page + */ + public static native int ioctl(int filedes, long request, int[] params); + + public static native int ioctl(int filedes, long request, WinSize params); + + public static short getTerminalWidth(int fd) { + WinSize sz = new WinSize(); + ioctl(fd, TIOCGWINSZ, sz); + return sz.ws_col; + } + + /** + * Window sizes. + * + * @see IOCTL_TTY(2) man-page + */ + public static class WinSize { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public short ws_row; + public short ws_col; + public short ws_xpixel; + public short ws_ypixel; + + public WinSize() {} + + public WinSize(short ws_row, short ws_col) { + this.ws_row = ws_row; + this.ws_col = ws_col; + } + } + + /** + * termios structure for termios functions, describing a general terminal interface that is + * provided to control asynchronous communications ports + * + * @see TERMIOS(3) man-page + */ + public static class Termios { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public long c_iflag; + public long c_oflag; + public long c_cflag; + public long c_lflag; + public byte[] c_cc = new byte[32]; + public long c_ispeed; + public long c_ospeed; + } +} diff --git a/native/src/main/java/org/jline/nativ/Kernel32.java b/native/src/main/java/org/jline/nativ/Kernel32.java new file mode 100644 index 000000000..66a525ffc --- /dev/null +++ b/native/src/main/java/org/jline/nativ/Kernel32.java @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2009-2023, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.nativ; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * Interface to access Win32 base APIs. + */ +@SuppressWarnings("unused") +public class Kernel32 { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static short FOREGROUND_BLUE; + public static short FOREGROUND_GREEN; + public static short FOREGROUND_RED; + public static short FOREGROUND_INTENSITY; + public static short BACKGROUND_BLUE; + public static short BACKGROUND_GREEN; + public static short BACKGROUND_RED; + public static short BACKGROUND_INTENSITY; + public static short COMMON_LVB_LEADING_BYTE; + public static short COMMON_LVB_TRAILING_BYTE; + public static short COMMON_LVB_GRID_HORIZONTAL; + public static short COMMON_LVB_GRID_LVERTICAL; + public static short COMMON_LVB_GRID_RVERTICAL; + public static short COMMON_LVB_REVERSE_VIDEO; + public static short COMMON_LVB_UNDERSCORE; + public static int FORMAT_MESSAGE_FROM_SYSTEM; + public static int STD_INPUT_HANDLE; + public static int STD_OUTPUT_HANDLE; + public static int STD_ERROR_HANDLE; + public static int INVALID_HANDLE_VALUE; + + public static native long malloc(long size); + + public static native void free(long ptr); + + /** + * http://msdn.microsoft.com/en-us/library/ms686311%28VS.85%29.aspx + */ + public static class SMALL_RECT { + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public short left; + public short top; + public short right; + public short bottom; + + public short width() { + return (short) (right - left); + } + + public short height() { + return (short) (bottom - top); + } + + public SMALL_RECT copy() { + SMALL_RECT rc = new SMALL_RECT(); + rc.left = left; + rc.top = top; + rc.right = right; + rc.bottom = bottom; + return rc; + } + } + + /** + * see http://msdn.microsoft.com/en-us/library/ms686047%28VS.85%29.aspx + */ + public static native int SetConsoleTextAttribute(long consoleOutput, short attributes); + + public static class COORD { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public short x; + public short y; + + public COORD copy() { + COORD rc = new COORD(); + rc.x = x; + rc.y = y; + return rc; + } + } + + /** + * http://msdn.microsoft.com/en-us/library/ms682093%28VS.85%29.aspx + */ + public static class CONSOLE_SCREEN_BUFFER_INFO { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public COORD size = new COORD(); + public COORD cursorPosition = new COORD(); + public short attributes; + public SMALL_RECT window = new SMALL_RECT(); + public COORD maximumWindowSize = new COORD(); + + public int windowWidth() { + return window.width() + 1; + } + + public int windowHeight() { + return window.height() + 1; + } + } + + // DWORD WINAPI WaitForSingleObject( + // _In_ HANDLE hHandle, + // _In_ DWORD dwMilliseconds + // ); + public static native int WaitForSingleObject(long hHandle, int dwMilliseconds); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms724211%28VS.85%29.aspx + */ + public static native int CloseHandle(long handle); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms679360(VS.85).aspx + */ + public static native int GetLastError(); + + public static native int FormatMessageW( + int flags, long source, int messageId, int languageId, byte[] buffer, int size, long[] args); + + /** + * See: http://msdn.microsoft.com/en-us/library/ms683171%28VS.85%29.aspx + */ + public static native int GetConsoleScreenBufferInfo( + long consoleOutput, CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683231%28VS.85%29.aspx + */ + public static native long GetStdHandle(int stdHandle); + + /** + * http://msdn.microsoft.com/en-us/library/ms686025%28VS.85%29.aspx + */ + public static native int SetConsoleCursorPosition(long consoleOutput, COORD cursorPosition); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms682663%28VS.85%29.aspx + */ + public static native int FillConsoleOutputCharacterW( + long consoleOutput, char character, int length, COORD writeCoord, int[] numberOfCharsWritten); + + /** + * see: https://msdn.microsoft.com/en-us/library/ms682662%28VS.85%29.aspx + */ + public static native int FillConsoleOutputAttribute( + long consoleOutput, short attribute, int length, COORD writeCoord, int[] numberOfAttrsWritten); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms687401(v=VS.85).aspx + */ + public static native int WriteConsoleW( + long consoleOutput, char[] buffer, int numberOfCharsToWrite, int[] numberOfCharsWritten, long reserved); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683167%28VS.85%29.aspx + */ + public static native int GetConsoleMode(long handle, int[] mode); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms686033%28VS.85%29.aspx + */ + public static native int SetConsoleMode(long handle, int mode); + + /** + * see: http://msdn.microsoft.com/en-us/library/078sfkak(VS.80).aspx + */ + public static native int _getch(); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms686050%28VS.85%29.aspx + * + * @return 0 if title was set successfully + */ + public static native int SetConsoleTitle(String title); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683169(v=VS.85).aspx + * + * @return the current output code page + */ + public static native int GetConsoleOutputCP(); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms686036(v=VS.85).aspx + * + * @return non 0 if code page was set + */ + public static native int SetConsoleOutputCP(int codePageID); + + /** + * see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682013(v=vs.85).aspx + */ + public static class CHAR_INFO { + + static { + JLineNativeLoader.initialize(); + init(); + } + + private static native void init(); + + public static int SIZEOF; + + public short attributes; + public char unicodeChar; + } + + /** + * see: https://msdn.microsoft.com/en-us/library/windows/desktop/ms685107(v=vs.85).aspx + */ + public static native int ScrollConsoleScreenBuffer( + long consoleOutput, + SMALL_RECT scrollRectangle, + SMALL_RECT clipRectangle, + COORD destinationOrigin, + CHAR_INFO fill); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms684166(v=VS.85).aspx + */ + public static class KEY_EVENT_RECORD { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + public static int CAPSLOCK_ON; + public static int NUMLOCK_ON; + public static int SCROLLLOCK_ON; + public static int ENHANCED_KEY; + public static int LEFT_ALT_PRESSED; + public static int LEFT_CTRL_PRESSED; + public static int RIGHT_ALT_PRESSED; + public static int RIGHT_CTRL_PRESSED; + public static int SHIFT_PRESSED; + + public boolean keyDown; + public short repeatCount; + public short keyCode; + public short scanCode; + public char uchar; + public int controlKeyState; + + public String toString() { + return "KEY_EVENT_RECORD{" + "keyDown=" + + keyDown + ", repeatCount=" + + repeatCount + ", keyCode=" + + keyCode + ", scanCode=" + + scanCode + ", uchar=" + + uchar + ", controlKeyState=" + + controlKeyState + '}'; + } + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms684239(v=VS.85).aspx + */ + public static class MOUSE_EVENT_RECORD { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + public static int FROM_LEFT_1ST_BUTTON_PRESSED; + public static int FROM_LEFT_2ND_BUTTON_PRESSED; + public static int FROM_LEFT_3RD_BUTTON_PRESSED; + public static int FROM_LEFT_4TH_BUTTON_PRESSED; + public static int RIGHTMOST_BUTTON_PRESSED; + + public static int CAPSLOCK_ON; + public static int NUMLOCK_ON; + public static int SCROLLLOCK_ON; + public static int ENHANCED_KEY; + public static int LEFT_ALT_PRESSED; + public static int LEFT_CTRL_PRESSED; + public static int RIGHT_ALT_PRESSED; + public static int RIGHT_CTRL_PRESSED; + public static int SHIFT_PRESSED; + + public static int DOUBLE_CLICK; + public static int MOUSE_HWHEELED; + public static int MOUSE_MOVED; + public static int MOUSE_WHEELED; + + public COORD mousePosition = new COORD(); + public int buttonState; + public int controlKeyState; + public int eventFlags; + + public String toString() { + return "MOUSE_EVENT_RECORD{" + "mousePosition=" + + mousePosition + ", buttonState=" + + buttonState + ", controlKeyState=" + + controlKeyState + ", eventFlags=" + + eventFlags + '}'; + } + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms687093(v=VS.85).aspx + */ + public static class WINDOW_BUFFER_SIZE_RECORD { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + + public COORD size = new COORD(); + + public String toString() { + return "WINDOW_BUFFER_SIZE_RECORD{size=" + size + '}'; + } + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683149(v=VS.85).aspx + */ + public static class FOCUS_EVENT_RECORD { + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + public boolean setFocus; + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms684213(v=VS.85).aspx + */ + public static class MENU_EVENT_RECORD { + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + public int commandId; + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683499(v=VS.85).aspx + */ + public static class INPUT_RECORD { + + static { + if (JLineNativeLoader.initialize()) { + init(); + } + } + + private static native void init(); + + public static int SIZEOF; + public static short KEY_EVENT; + public static short MOUSE_EVENT; + public static short WINDOW_BUFFER_SIZE_EVENT; + public static short FOCUS_EVENT; + public static short MENU_EVENT; + public short eventType; + public KEY_EVENT_RECORD keyEvent = new KEY_EVENT_RECORD(); + public MOUSE_EVENT_RECORD mouseEvent = new MOUSE_EVENT_RECORD(); + public WINDOW_BUFFER_SIZE_RECORD windowBufferSizeEvent = new WINDOW_BUFFER_SIZE_RECORD(); + public MENU_EVENT_RECORD menuEvent = new MENU_EVENT_RECORD(); + public FOCUS_EVENT_RECORD focusEvent = new FOCUS_EVENT_RECORD(); + + public static native void memmove(INPUT_RECORD dest, long src, long size); + } + + /** + * see: http://msdn.microsoft.com/en-us/library/ms684961(v=VS.85).aspx + */ + private static native int ReadConsoleInputW(long handle, long inputRecord, int length, int[] eventsCount); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms684344(v=VS.85).aspx + */ + private static native int PeekConsoleInputW(long handle, long inputRecord, int length, int[] eventsCount); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683207(v=VS.85).aspx + */ + public static native int GetNumberOfConsoleInputEvents(long handle, int[] numberOfEvents); + + /** + * see: http://msdn.microsoft.com/en-us/library/ms683147(v=VS.85).aspx + */ + public static native int FlushConsoleInputBuffer(long handle); + + /** + * Return console input events. + */ + public static INPUT_RECORD[] readConsoleInputHelper(long handle, int count, boolean peek) throws IOException { + int[] length = new int[1]; + int res; + long inputRecordPtr = 0; + try { + inputRecordPtr = malloc(INPUT_RECORD.SIZEOF * count); + if (inputRecordPtr == 0) { + throw new IOException("cannot allocate memory with JNI"); + } + res = peek + ? PeekConsoleInputW(handle, inputRecordPtr, count, length) + : ReadConsoleInputW(handle, inputRecordPtr, count, length); + if (res == 0) { + int bufferSize = 160; + byte[] data = new byte[bufferSize]; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, data, bufferSize, null); + String lastErrorMessage = new String(data, StandardCharsets.UTF_16LE).trim(); + throw new IOException("ReadConsoleInputW failed: " + lastErrorMessage); + } + if (length[0] <= 0) { + return new INPUT_RECORD[0]; + } + INPUT_RECORD[] records = new INPUT_RECORD[length[0]]; + for (int i = 0; i < records.length; i++) { + records[i] = new INPUT_RECORD(); + INPUT_RECORD.memmove(records[i], inputRecordPtr + i * INPUT_RECORD.SIZEOF, INPUT_RECORD.SIZEOF); + } + return records; + } finally { + if (inputRecordPtr != 0) { + free(inputRecordPtr); + } + } + } + + /** + * Return console input key events (discard other events). + * + * @param count requested number of events + * @return array possibly of size smaller then count + */ + public static INPUT_RECORD[] readConsoleKeyInput(long handle, int count, boolean peek) throws IOException { + while (true) { + // read events until we have keyboard events, the queue could be full + // of mouse events. + INPUT_RECORD[] evts = readConsoleInputHelper(handle, count, peek); + int keyEvtCount = 0; + for (INPUT_RECORD evt : evts) { + if (evt.eventType == INPUT_RECORD.KEY_EVENT) keyEvtCount++; + } + if (keyEvtCount > 0) { + INPUT_RECORD[] res = new INPUT_RECORD[keyEvtCount]; + int i = 0; + for (INPUT_RECORD evt : evts) { + if (evt.eventType == INPUT_RECORD.KEY_EVENT) { + res[i++] = evt; + } + } + return res; + } + } + } + + public static String getLastErrorMessage() { + int bufferSize = 160; + byte[] data = new byte[bufferSize]; + FormatMessageW(Kernel32.FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, data, bufferSize, null); + return new String(data, StandardCharsets.UTF_16LE).trim(); + } + + public static native int isatty(int fd); +} diff --git a/native/src/main/native/clibrary.c b/native/src/main/native/clibrary.c new file mode 100644 index 000000000..59b9c14d3 --- /dev/null +++ b/native/src/main/native/clibrary.c @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (C) 2009-2017 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. + *******************************************************************************/ +#include "jlinenative.h" + +#if !defined(_WIN32) && !defined(_WIN64) + +#define Termios_NATIVE(func) Java_org_jline_nativ_CLibrary_00024Termios_##func +#define WinSize_NATIVE(func) Java_org_jline_nativ_CLibrary_00024WinSize_##func +#define CLibrary_NATIVE(func) Java_org_jline_nativ_CLibrary_##func + +typedef struct Termios_FID_CACHE { + int cached; + jclass clazz; + jfieldID c_iflag, c_oflag, c_cflag, c_lflag, c_cc, c_ispeed, c_ospeed; +} Termios_FID_CACHE; + +Termios_FID_CACHE TermiosFc; + +void cacheTermiosFields(JNIEnv *env, jobject lpObject) +{ + if (TermiosFc.cached) return; + TermiosFc.clazz = (*env)->GetObjectClass(env, lpObject); + TermiosFc.c_iflag = (*env)->GetFieldID(env, TermiosFc.clazz, "c_iflag", "J"); + TermiosFc.c_oflag = (*env)->GetFieldID(env, TermiosFc.clazz, "c_oflag", "J"); + TermiosFc.c_cflag = (*env)->GetFieldID(env, TermiosFc.clazz, "c_cflag", "J"); + TermiosFc.c_lflag = (*env)->GetFieldID(env, TermiosFc.clazz, "c_lflag", "J"); + TermiosFc.c_cc = (*env)->GetFieldID(env, TermiosFc.clazz, "c_cc", "[B"); + TermiosFc.c_ispeed = (*env)->GetFieldID(env, TermiosFc.clazz, "c_ispeed", "J"); + TermiosFc.c_ospeed = (*env)->GetFieldID(env, TermiosFc.clazz, "c_ospeed", "J"); + hawtjni_w_barrier(); + TermiosFc.cached = 1; +} + +struct termios *getTermiosFields(JNIEnv *env, jobject lpObject, struct termios *lpStruct) +{ + if (!TermiosFc.cached) cacheTermiosFields(env, lpObject); + lpStruct->c_iflag = (*env)->GetLongField(env, lpObject, TermiosFc.c_iflag); + lpStruct->c_oflag = (*env)->GetLongField(env, lpObject, TermiosFc.c_oflag); + lpStruct->c_cflag = (*env)->GetLongField(env, lpObject, TermiosFc.c_cflag); + lpStruct->c_lflag = (*env)->GetLongField(env, lpObject, TermiosFc.c_lflag); + { + jbyteArray lpObject1 = (jbyteArray)(*env)->GetObjectField(env, lpObject, TermiosFc.c_cc); + (*env)->GetByteArrayRegion(env, lpObject1, 0, sizeof(lpStruct->c_cc), (jbyte *)lpStruct->c_cc); + } + lpStruct->c_ispeed = (*env)->GetLongField(env, lpObject, TermiosFc.c_ispeed); + lpStruct->c_ospeed = (*env)->GetLongField(env, lpObject, TermiosFc.c_ospeed); + return lpStruct; +} + +void setTermiosFields(JNIEnv *env, jobject lpObject, struct termios *lpStruct) +{ + if (!TermiosFc.cached) cacheTermiosFields(env, lpObject); + (*env)->SetLongField(env, lpObject, TermiosFc.c_iflag, (jlong)lpStruct->c_iflag); + (*env)->SetLongField(env, lpObject, TermiosFc.c_oflag, (jlong)lpStruct->c_oflag); + (*env)->SetLongField(env, lpObject, TermiosFc.c_cflag, (jlong)lpStruct->c_cflag); + (*env)->SetLongField(env, lpObject, TermiosFc.c_lflag, (jlong)lpStruct->c_lflag); + { + jbyteArray lpObject1 = (jbyteArray)(*env)->GetObjectField(env, lpObject, TermiosFc.c_cc); + (*env)->SetByteArrayRegion(env, lpObject1, 0, sizeof(lpStruct->c_cc), (jbyte *)lpStruct->c_cc); + } + (*env)->SetLongField(env, lpObject, TermiosFc.c_ispeed, (jlong)lpStruct->c_ispeed); + (*env)->SetLongField(env, lpObject, TermiosFc.c_ospeed, (jlong)lpStruct->c_ospeed); +} + +typedef struct WinSize_FID_CACHE { + int cached; + jclass clazz; + jfieldID ws_row, ws_col, ws_xpixel, ws_ypixel; +} WinSize_FID_CACHE; + +WinSize_FID_CACHE WinSizeFc; + +void cacheWinSizeFields(JNIEnv *env, jobject lpObject) +{ + if (WinSizeFc.cached) return; + WinSizeFc.clazz = (*env)->GetObjectClass(env, lpObject); + WinSizeFc.ws_row = (*env)->GetFieldID(env, WinSizeFc.clazz, "ws_row", "S"); + WinSizeFc.ws_col = (*env)->GetFieldID(env, WinSizeFc.clazz, "ws_col", "S"); + WinSizeFc.ws_xpixel = (*env)->GetFieldID(env, WinSizeFc.clazz, "ws_xpixel", "S"); + WinSizeFc.ws_ypixel = (*env)->GetFieldID(env, WinSizeFc.clazz, "ws_ypixel", "S"); + hawtjni_w_barrier(); + WinSizeFc.cached = 1; +} + +struct winsize *getWinSizeFields(JNIEnv *env, jobject lpObject, struct winsize *lpStruct) +{ + if (!WinSizeFc.cached) cacheWinSizeFields(env, lpObject); + lpStruct->ws_row = (*env)->GetShortField(env, lpObject, WinSizeFc.ws_row); + lpStruct->ws_col = (*env)->GetShortField(env, lpObject, WinSizeFc.ws_col); + lpStruct->ws_xpixel = (*env)->GetShortField(env, lpObject, WinSizeFc.ws_xpixel); + lpStruct->ws_ypixel = (*env)->GetShortField(env, lpObject, WinSizeFc.ws_ypixel); + return lpStruct; +} + +void setWinSizeFields(JNIEnv *env, jobject lpObject, struct winsize *lpStruct) +{ + if (!WinSizeFc.cached) cacheWinSizeFields(env, lpObject); + (*env)->SetShortField(env, lpObject, WinSizeFc.ws_row, (jshort)lpStruct->ws_row); + (*env)->SetShortField(env, lpObject, WinSizeFc.ws_col, (jshort)lpStruct->ws_col); + (*env)->SetShortField(env, lpObject, WinSizeFc.ws_xpixel, (jshort)lpStruct->ws_xpixel); + (*env)->SetShortField(env, lpObject, WinSizeFc.ws_ypixel, (jshort)lpStruct->ws_ypixel); +} + +JNIEXPORT void JNICALL Termios_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(struct termios)); + return; +} + +JNIEXPORT void JNICALL WinSize_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(struct winsize)); + return; +} + +JNIEXPORT void JNICALL CLibrary_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "TCSANOW", "I"), (jint)TCSANOW); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "TCSADRAIN", "I"), (jint)TCSADRAIN); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "TCSAFLUSH", "I"), (jint)TCSAFLUSH); + (*env)->SetStaticLongField(env, that, (*env)->GetStaticFieldID(env, that, "TIOCGWINSZ", "J"), (jlong)TIOCGWINSZ); + (*env)->SetStaticLongField(env, that, (*env)->GetStaticFieldID(env, that, "TIOCSWINSZ", "J"), (jlong)TIOCSWINSZ); + return; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(ioctl__IJLorg_jline_nativ_CLibrary_00024WinSize_2) + (JNIEnv *env, jclass that, jint arg0, jlong arg1, jobject arg2) +{ + struct winsize _arg2, *lparg2=NULL; + jint rc = 0; + + if (arg2) if ((lparg2 = getWinSizeFields(env, arg2, &_arg2)) == NULL) goto fail; + rc = (jint)ioctl(arg0, arg1, (intptr_t)lparg2); +fail: + if (arg2 && lparg2) setWinSizeFields(env, arg2, lparg2); + + return rc; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(ioctl__IJ_3I) + (JNIEnv *env, jclass that, jint arg0, jlong arg1, jintArray arg2) +{ + jint *lparg2=NULL; + jint rc = 0; + + if (arg2) if ((lparg2 = (*env)->GetIntArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jint)ioctl(arg0, arg1, lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseIntArrayElements(env, arg2, lparg2, 0); + + return rc; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(openpty) + (JNIEnv *env, jclass that, jintArray arg0, jintArray arg1, jbyteArray arg2, jobject arg3, jobject arg4) +{ + jint *lparg0=NULL; + jint *lparg1=NULL; + jbyte *lparg2=NULL; + struct termios _arg3, *lparg3=NULL; + struct winsize _arg4, *lparg4=NULL; + jint rc = 0; + + if (arg0) if ((lparg0 = (*env)->GetIntArrayElements(env, arg0, NULL)) == NULL) goto fail; + if (arg1) if ((lparg1 = (*env)->GetIntArrayElements(env, arg1, NULL)) == NULL) goto fail; + if (arg2) if ((lparg2 = (*env)->GetByteArrayElements(env, arg2, NULL)) == NULL) goto fail; + if (arg3) if ((lparg3 = getTermiosFields(env, arg3, &_arg3)) == NULL) goto fail; + if (arg4) if ((lparg4 = getWinSizeFields(env, arg4, &_arg4)) == NULL) goto fail; + rc = (jint)openpty((int *)lparg0, (int *)lparg1, (char *)lparg2, (struct termios *)lparg3, (struct winsize *)lparg4); +fail: + if (arg2 && lparg2) (*env)->ReleaseByteArrayElements(env, arg2, lparg2, 0); + if (arg1 && lparg1) (*env)->ReleaseIntArrayElements(env, arg1, lparg1, 0); + if (arg0 && lparg0) (*env)->ReleaseIntArrayElements(env, arg0, lparg0, 0); + + return rc; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(tcgetattr) + (JNIEnv *env, jclass that, jint arg0, jobject arg1) +{ + struct termios _arg1, *lparg1=NULL; + jint rc = 0; + + if (arg1) if ((lparg1 = &_arg1) == NULL) goto fail; + rc = (jint)tcgetattr(arg0, (struct termios *)lparg1); +fail: + if (arg1 && lparg1) setTermiosFields(env, arg1, lparg1); + + return rc; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(tcsetattr) + (JNIEnv *env, jclass that, jint arg0, jint arg1, jobject arg2) +{ + struct termios _arg2, *lparg2=NULL; + jint rc = 0; + + if (arg2) if ((lparg2 = getTermiosFields(env, arg2, &_arg2)) == NULL) goto fail; + rc = (jint)tcsetattr(arg0, arg1, (struct termios *)lparg2); +fail: + + return rc; +} + +JNIEXPORT jint JNICALL CLibrary_NATIVE(isatty) + (JNIEnv *env, jclass that, jint arg0) +{ + jint rc = 0; + + rc = (jint)isatty(arg0); + + return rc; +} + +JNIEXPORT jstring JNICALL CLibrary_NATIVE(ttyname) + (JNIEnv *env, jclass that, jint arg0) +{ + jstring rc = 0; + char s[256] = { 0 }; + int r = 0; + + r = ttyname_r(arg0, s, 256); + if (!r) rc = (*env)->NewStringUTF(env,s); + + return rc; +} + +#endif diff --git a/native/src/main/native/kernel32.c b/native/src/main/native/kernel32.c new file mode 100644 index 000000000..003cdcbaa --- /dev/null +++ b/native/src/main/native/kernel32.c @@ -0,0 +1,974 @@ +/******************************************************************************* + * Copyright (C) 2009-2017 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. + *******************************************************************************/ +#include "jlinenative.h" + +#if defined(_WIN32) || defined(_WIN64) + +#define isatty _isatty + +#define Kernel32_NATIVE(func) Java_org_jline_nativ_Kernel32_##func +#define CHAR_INFO_NATIVE(func) Java_org_jline_nativ_Kernel32_00024CHAR_1INFO_##func +#define CONSOLE_SCREEN_BUFFER_INFO_NATIVE(func) Java_org_jline_nativ_Kernel32_00024CONSOLE_1SCREEN_1BUFFER_1INFO_##func +#define COORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024COORD_##func +#define FOCUS_EVENT_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024FOCUS_1EVENT_1RECORD_##func +#define INPUT_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024INPUT_1RECORD_##func +#define KEY_EVENT_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024KEY_1EVENT_1RECORD_##func +#define MENU_EVENT_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024MENU_1EVENT_1RECORD_##func +#define MOUSE_EVENT_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024MOUSE_1EVENT_1RECORD_##func +#define SMALL_RECT_NATIVE(func) Java_org_jline_nativ_Kernel32_00024SMALL_1RECT_##func +#define WINDOW_BUFFER_SIZE_RECORD_NATIVE(func) Java_org_jline_nativ_Kernel32_00024WINDOW_1BUFFER_1SIZE_1RECORD_##func + +void cacheCHAR_INFOFields(JNIEnv *env, jobject lpObject); +CHAR_INFO *getCHAR_INFOFields(JNIEnv *env, jobject lpObject, CHAR_INFO *lpStruct); +void setCHAR_INFOFields(JNIEnv *env, jobject lpObject, CHAR_INFO *lpStruct); + +void cacheCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject); +CONSOLE_SCREEN_BUFFER_INFO *getCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject, CONSOLE_SCREEN_BUFFER_INFO *lpStruct); +void setCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject, CONSOLE_SCREEN_BUFFER_INFO *lpStruct); + +void cacheCOORDFields(JNIEnv *env, jobject lpObject); +COORD *getCOORDFields(JNIEnv *env, jobject lpObject, COORD *lpStruct); +void setCOORDFields(JNIEnv *env, jobject lpObject, COORD *lpStruct); + +void cacheFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject); +FOCUS_EVENT_RECORD *getFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, FOCUS_EVENT_RECORD *lpStruct); +void setFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, FOCUS_EVENT_RECORD *lpStruct); + +void cacheINPUT_RECORDFields(JNIEnv *env, jobject lpObject); +INPUT_RECORD *getINPUT_RECORDFields(JNIEnv *env, jobject lpObject, INPUT_RECORD *lpStruct); +void setINPUT_RECORDFields(JNIEnv *env, jobject lpObject, INPUT_RECORD *lpStruct); + +void cacheKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject); +KEY_EVENT_RECORD *getKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, KEY_EVENT_RECORD *lpStruct); +void setKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, KEY_EVENT_RECORD *lpStruct); + +void cacheMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject); +MENU_EVENT_RECORD *getMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MENU_EVENT_RECORD *lpStruct); +void setMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MENU_EVENT_RECORD *lpStruct); + +void cacheMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject); +MOUSE_EVENT_RECORD *getMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MOUSE_EVENT_RECORD *lpStruct); +void setMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MOUSE_EVENT_RECORD *lpStruct); + +void cacheSMALL_RECTFields(JNIEnv *env, jobject lpObject); +SMALL_RECT *getSMALL_RECTFields(JNIEnv *env, jobject lpObject, SMALL_RECT *lpStruct); +void setSMALL_RECTFields(JNIEnv *env, jobject lpObject, SMALL_RECT *lpStruct); + +void cacheWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject); +WINDOW_BUFFER_SIZE_RECORD *getWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject, WINDOW_BUFFER_SIZE_RECORD *lpStruct); +void setWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject, WINDOW_BUFFER_SIZE_RECORD *lpStruct); + +typedef struct CHAR_INFO_FID_CACHE { + int cached; + jclass clazz; + jfieldID attributes, unicodeChar; +} CHAR_INFO_FID_CACHE; + +CHAR_INFO_FID_CACHE CHAR_INFOFc; + +void cacheCHAR_INFOFields(JNIEnv *env, jobject lpObject) +{ + if (CHAR_INFOFc.cached) return; + CHAR_INFOFc.clazz = (*env)->GetObjectClass(env, lpObject); + CHAR_INFOFc.attributes = (*env)->GetFieldID(env, CHAR_INFOFc.clazz, "attributes", "S"); + CHAR_INFOFc.unicodeChar = (*env)->GetFieldID(env, CHAR_INFOFc.clazz, "unicodeChar", "C"); + hawtjni_w_barrier(); + CHAR_INFOFc.cached = 1; +} + +CHAR_INFO *getCHAR_INFOFields(JNIEnv *env, jobject lpObject, CHAR_INFO *lpStruct) +{ + if (!CHAR_INFOFc.cached) cacheCHAR_INFOFields(env, lpObject); + lpStruct->Attributes = (*env)->GetShortField(env, lpObject, CHAR_INFOFc.attributes); + lpStruct->Char.UnicodeChar = (*env)->GetCharField(env, lpObject, CHAR_INFOFc.unicodeChar); + return lpStruct; +} + +void setCHAR_INFOFields(JNIEnv *env, jobject lpObject, CHAR_INFO *lpStruct) +{ + if (!CHAR_INFOFc.cached) cacheCHAR_INFOFields(env, lpObject); + (*env)->SetShortField(env, lpObject, CHAR_INFOFc.attributes, (jshort)lpStruct->Attributes); + (*env)->SetCharField(env, lpObject, CHAR_INFOFc.unicodeChar, (jchar)lpStruct->Char.UnicodeChar); +} + +typedef struct CONSOLE_SCREEN_BUFFER_INFO_FID_CACHE { + int cached; + jclass clazz; + jfieldID size, cursorPosition, attributes, window, maximumWindowSize; +} CONSOLE_SCREEN_BUFFER_INFO_FID_CACHE; + +CONSOLE_SCREEN_BUFFER_INFO_FID_CACHE CONSOLE_SCREEN_BUFFER_INFOFc; + +void cacheCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject) +{ + if (CONSOLE_SCREEN_BUFFER_INFOFc.cached) return; + CONSOLE_SCREEN_BUFFER_INFOFc.clazz = (*env)->GetObjectClass(env, lpObject); + CONSOLE_SCREEN_BUFFER_INFOFc.size = (*env)->GetFieldID(env, CONSOLE_SCREEN_BUFFER_INFOFc.clazz, "size", "Lorg/jline/nativ/Kernel32$COORD;"); + CONSOLE_SCREEN_BUFFER_INFOFc.cursorPosition = (*env)->GetFieldID(env, CONSOLE_SCREEN_BUFFER_INFOFc.clazz, "cursorPosition", "Lorg/jline/nativ/Kernel32$COORD;"); + CONSOLE_SCREEN_BUFFER_INFOFc.attributes = (*env)->GetFieldID(env, CONSOLE_SCREEN_BUFFER_INFOFc.clazz, "attributes", "S"); + CONSOLE_SCREEN_BUFFER_INFOFc.window = (*env)->GetFieldID(env, CONSOLE_SCREEN_BUFFER_INFOFc.clazz, "window", "Lorg/jline/nativ/Kernel32$SMALL_RECT;"); + CONSOLE_SCREEN_BUFFER_INFOFc.maximumWindowSize = (*env)->GetFieldID(env, CONSOLE_SCREEN_BUFFER_INFOFc.clazz, "maximumWindowSize", "Lorg/jline/nativ/Kernel32$COORD;"); + hawtjni_w_barrier(); + CONSOLE_SCREEN_BUFFER_INFOFc.cached = 1; +} + +CONSOLE_SCREEN_BUFFER_INFO *getCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject, CONSOLE_SCREEN_BUFFER_INFO *lpStruct) +{ + if (!CONSOLE_SCREEN_BUFFER_INFOFc.cached) cacheCONSOLE_SCREEN_BUFFER_INFOFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.size); + if (lpObject1 != NULL) getCOORDFields(env, lpObject1, &lpStruct->dwSize); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.cursorPosition); + if (lpObject1 != NULL) getCOORDFields(env, lpObject1, &lpStruct->dwCursorPosition); + } + lpStruct->wAttributes = (*env)->GetShortField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.attributes); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.window); + if (lpObject1 != NULL) getSMALL_RECTFields(env, lpObject1, &lpStruct->srWindow); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.maximumWindowSize); + if (lpObject1 != NULL) getCOORDFields(env, lpObject1, &lpStruct->dwMaximumWindowSize); + } + return lpStruct; +} + +void setCONSOLE_SCREEN_BUFFER_INFOFields(JNIEnv *env, jobject lpObject, CONSOLE_SCREEN_BUFFER_INFO *lpStruct) +{ + if (!CONSOLE_SCREEN_BUFFER_INFOFc.cached) cacheCONSOLE_SCREEN_BUFFER_INFOFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.size); + if (lpObject1 != NULL) setCOORDFields(env, lpObject1, &lpStruct->dwSize); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.cursorPosition); + if (lpObject1 != NULL) setCOORDFields(env, lpObject1, &lpStruct->dwCursorPosition); + } + (*env)->SetShortField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.attributes, (jshort)lpStruct->wAttributes); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.window); + if (lpObject1 != NULL) setSMALL_RECTFields(env, lpObject1, &lpStruct->srWindow); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, CONSOLE_SCREEN_BUFFER_INFOFc.maximumWindowSize); + if (lpObject1 != NULL) setCOORDFields(env, lpObject1, &lpStruct->dwMaximumWindowSize); + } +} + +typedef struct COORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID x, y; +} COORD_FID_CACHE; + +COORD_FID_CACHE COORDFc; + +void cacheCOORDFields(JNIEnv *env, jobject lpObject) +{ + if (COORDFc.cached) return; + COORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + COORDFc.x = (*env)->GetFieldID(env, COORDFc.clazz, "x", "S"); + COORDFc.y = (*env)->GetFieldID(env, COORDFc.clazz, "y", "S"); + hawtjni_w_barrier(); + COORDFc.cached = 1; +} + +COORD *getCOORDFields(JNIEnv *env, jobject lpObject, COORD *lpStruct) +{ + if (!COORDFc.cached) cacheCOORDFields(env, lpObject); + lpStruct->X = (*env)->GetShortField(env, lpObject, COORDFc.x); + lpStruct->Y = (*env)->GetShortField(env, lpObject, COORDFc.y); + return lpStruct; +} + +void setCOORDFields(JNIEnv *env, jobject lpObject, COORD *lpStruct) +{ + if (!COORDFc.cached) cacheCOORDFields(env, lpObject); + (*env)->SetShortField(env, lpObject, COORDFc.x, (jshort)lpStruct->X); + (*env)->SetShortField(env, lpObject, COORDFc.y, (jshort)lpStruct->Y); +} + +typedef struct FOCUS_EVENT_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID setFocus; +} FOCUS_EVENT_RECORD_FID_CACHE; + +FOCUS_EVENT_RECORD_FID_CACHE FOCUS_EVENT_RECORDFc; + +void cacheFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (FOCUS_EVENT_RECORDFc.cached) return; + FOCUS_EVENT_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + FOCUS_EVENT_RECORDFc.setFocus = (*env)->GetFieldID(env, FOCUS_EVENT_RECORDFc.clazz, "setFocus", "Z"); + hawtjni_w_barrier(); + FOCUS_EVENT_RECORDFc.cached = 1; +} + +FOCUS_EVENT_RECORD *getFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, FOCUS_EVENT_RECORD *lpStruct) +{ + if (!FOCUS_EVENT_RECORDFc.cached) cacheFOCUS_EVENT_RECORDFields(env, lpObject); + lpStruct->bSetFocus = (*env)->GetBooleanField(env, lpObject, FOCUS_EVENT_RECORDFc.setFocus); + return lpStruct; +} + +void setFOCUS_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, FOCUS_EVENT_RECORD *lpStruct) +{ + if (!FOCUS_EVENT_RECORDFc.cached) cacheFOCUS_EVENT_RECORDFields(env, lpObject); + (*env)->SetBooleanField(env, lpObject, FOCUS_EVENT_RECORDFc.setFocus, (jboolean)lpStruct->bSetFocus); +} + +typedef struct INPUT_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID eventType, keyEvent, mouseEvent, windowBufferSizeEvent, menuEvent, focusEvent; +} INPUT_RECORD_FID_CACHE; + +INPUT_RECORD_FID_CACHE INPUT_RECORDFc; + +void cacheINPUT_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (INPUT_RECORDFc.cached) return; + INPUT_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + INPUT_RECORDFc.eventType = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "eventType", "S"); + INPUT_RECORDFc.keyEvent = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "keyEvent", "Lorg/jline/nativ/Kernel32$KEY_EVENT_RECORD;"); + INPUT_RECORDFc.mouseEvent = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "mouseEvent", "Lorg/jline/nativ/Kernel32$MOUSE_EVENT_RECORD;"); + INPUT_RECORDFc.windowBufferSizeEvent = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "windowBufferSizeEvent", "Lorg/jline/nativ/Kernel32$WINDOW_BUFFER_SIZE_RECORD;"); + INPUT_RECORDFc.menuEvent = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "menuEvent", "Lorg/jline/nativ/Kernel32$MENU_EVENT_RECORD;"); + INPUT_RECORDFc.focusEvent = (*env)->GetFieldID(env, INPUT_RECORDFc.clazz, "focusEvent", "Lorg/jline/nativ/Kernel32$FOCUS_EVENT_RECORD;"); + hawtjni_w_barrier(); + INPUT_RECORDFc.cached = 1; +} + +INPUT_RECORD *getINPUT_RECORDFields(JNIEnv *env, jobject lpObject, INPUT_RECORD *lpStruct) +{ + if (!INPUT_RECORDFc.cached) cacheINPUT_RECORDFields(env, lpObject); + lpStruct->EventType = (*env)->GetShortField(env, lpObject, INPUT_RECORDFc.eventType); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.keyEvent); + if (lpObject1 != NULL) getKEY_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.KeyEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.mouseEvent); + if (lpObject1 != NULL) getMOUSE_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.MouseEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.windowBufferSizeEvent); + if (lpObject1 != NULL) getWINDOW_BUFFER_SIZE_RECORDFields(env, lpObject1, &lpStruct->Event.WindowBufferSizeEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.menuEvent); + if (lpObject1 != NULL) getMENU_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.MenuEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.focusEvent); + if (lpObject1 != NULL) getFOCUS_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.FocusEvent); + } + return lpStruct; +} + +void setINPUT_RECORDFields(JNIEnv *env, jobject lpObject, INPUT_RECORD *lpStruct) +{ + if (!INPUT_RECORDFc.cached) cacheINPUT_RECORDFields(env, lpObject); + (*env)->SetShortField(env, lpObject, INPUT_RECORDFc.eventType, (jshort)lpStruct->EventType); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.keyEvent); + if (lpObject1 != NULL) setKEY_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.KeyEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.mouseEvent); + if (lpObject1 != NULL) setMOUSE_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.MouseEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.windowBufferSizeEvent); + if (lpObject1 != NULL) setWINDOW_BUFFER_SIZE_RECORDFields(env, lpObject1, &lpStruct->Event.WindowBufferSizeEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.menuEvent); + if (lpObject1 != NULL) setMENU_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.MenuEvent); + } + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, INPUT_RECORDFc.focusEvent); + if (lpObject1 != NULL) setFOCUS_EVENT_RECORDFields(env, lpObject1, &lpStruct->Event.FocusEvent); + } +} + +typedef struct KEY_EVENT_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID keyDown, repeatCount, keyCode, scanCode, uchar, controlKeyState; +} KEY_EVENT_RECORD_FID_CACHE; + +KEY_EVENT_RECORD_FID_CACHE KEY_EVENT_RECORDFc; + +void cacheKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (KEY_EVENT_RECORDFc.cached) return; + KEY_EVENT_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + KEY_EVENT_RECORDFc.keyDown = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "keyDown", "Z"); + KEY_EVENT_RECORDFc.repeatCount = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "repeatCount", "S"); + KEY_EVENT_RECORDFc.keyCode = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "keyCode", "S"); + KEY_EVENT_RECORDFc.scanCode = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "scanCode", "S"); + KEY_EVENT_RECORDFc.uchar = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "uchar", "C"); + KEY_EVENT_RECORDFc.controlKeyState = (*env)->GetFieldID(env, KEY_EVENT_RECORDFc.clazz, "controlKeyState", "I"); + hawtjni_w_barrier(); + KEY_EVENT_RECORDFc.cached = 1; +} + +KEY_EVENT_RECORD *getKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, KEY_EVENT_RECORD *lpStruct) +{ + if (!KEY_EVENT_RECORDFc.cached) cacheKEY_EVENT_RECORDFields(env, lpObject); + lpStruct->bKeyDown = (*env)->GetBooleanField(env, lpObject, KEY_EVENT_RECORDFc.keyDown); + lpStruct->wRepeatCount = (*env)->GetShortField(env, lpObject, KEY_EVENT_RECORDFc.repeatCount); + lpStruct->wVirtualKeyCode = (*env)->GetShortField(env, lpObject, KEY_EVENT_RECORDFc.keyCode); + lpStruct->wVirtualScanCode = (*env)->GetShortField(env, lpObject, KEY_EVENT_RECORDFc.scanCode); + lpStruct->uChar.UnicodeChar = (*env)->GetCharField(env, lpObject, KEY_EVENT_RECORDFc.uchar); + lpStruct->dwControlKeyState = (*env)->GetIntField(env, lpObject, KEY_EVENT_RECORDFc.controlKeyState); + return lpStruct; +} + +void setKEY_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, KEY_EVENT_RECORD *lpStruct) +{ + if (!KEY_EVENT_RECORDFc.cached) cacheKEY_EVENT_RECORDFields(env, lpObject); + (*env)->SetBooleanField(env, lpObject, KEY_EVENT_RECORDFc.keyDown, (jboolean)lpStruct->bKeyDown); + (*env)->SetShortField(env, lpObject, KEY_EVENT_RECORDFc.repeatCount, (jshort)lpStruct->wRepeatCount); + (*env)->SetShortField(env, lpObject, KEY_EVENT_RECORDFc.keyCode, (jshort)lpStruct->wVirtualKeyCode); + (*env)->SetShortField(env, lpObject, KEY_EVENT_RECORDFc.scanCode, (jshort)lpStruct->wVirtualScanCode); + (*env)->SetCharField(env, lpObject, KEY_EVENT_RECORDFc.uchar, (jchar)lpStruct->uChar.UnicodeChar); + (*env)->SetIntField(env, lpObject, KEY_EVENT_RECORDFc.controlKeyState, (jint)lpStruct->dwControlKeyState); +} + +typedef struct MENU_EVENT_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID commandId; +} MENU_EVENT_RECORD_FID_CACHE; + +MENU_EVENT_RECORD_FID_CACHE MENU_EVENT_RECORDFc; + +void cacheMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (MENU_EVENT_RECORDFc.cached) return; + MENU_EVENT_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + MENU_EVENT_RECORDFc.commandId = (*env)->GetFieldID(env, MENU_EVENT_RECORDFc.clazz, "commandId", "I"); + hawtjni_w_barrier(); + MENU_EVENT_RECORDFc.cached = 1; +} + +MENU_EVENT_RECORD *getMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MENU_EVENT_RECORD *lpStruct) +{ + if (!MENU_EVENT_RECORDFc.cached) cacheMENU_EVENT_RECORDFields(env, lpObject); +#if defined(_WIN32) || defined(_WIN64) + lpStruct->dwCommandId = (*env)->GetIntField(env, lpObject, MENU_EVENT_RECORDFc.commandId); +#endif + return lpStruct; +} + +void setMENU_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MENU_EVENT_RECORD *lpStruct) +{ + if (!MENU_EVENT_RECORDFc.cached) cacheMENU_EVENT_RECORDFields(env, lpObject); + (*env)->SetIntField(env, lpObject, MENU_EVENT_RECORDFc.commandId, (jint)lpStruct->dwCommandId); +} + +typedef struct MOUSE_EVENT_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID mousePosition, buttonState, controlKeyState, eventFlags; +} MOUSE_EVENT_RECORD_FID_CACHE; + +MOUSE_EVENT_RECORD_FID_CACHE MOUSE_EVENT_RECORDFc; + +void cacheMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (MOUSE_EVENT_RECORDFc.cached) return; + MOUSE_EVENT_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + MOUSE_EVENT_RECORDFc.mousePosition = (*env)->GetFieldID(env, MOUSE_EVENT_RECORDFc.clazz, "mousePosition", "Lorg/jline/nativ/Kernel32$COORD;"); + MOUSE_EVENT_RECORDFc.buttonState = (*env)->GetFieldID(env, MOUSE_EVENT_RECORDFc.clazz, "buttonState", "I"); + MOUSE_EVENT_RECORDFc.controlKeyState = (*env)->GetFieldID(env, MOUSE_EVENT_RECORDFc.clazz, "controlKeyState", "I"); + MOUSE_EVENT_RECORDFc.eventFlags = (*env)->GetFieldID(env, MOUSE_EVENT_RECORDFc.clazz, "eventFlags", "I"); + hawtjni_w_barrier(); + MOUSE_EVENT_RECORDFc.cached = 1; +} + +MOUSE_EVENT_RECORD *getMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MOUSE_EVENT_RECORD *lpStruct) +{ + if (!MOUSE_EVENT_RECORDFc.cached) cacheMOUSE_EVENT_RECORDFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, MOUSE_EVENT_RECORDFc.mousePosition); + if (lpObject1 != NULL) getCOORDFields(env, lpObject1, &lpStruct->dwMousePosition); + } + lpStruct->dwButtonState = (*env)->GetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.buttonState); + lpStruct->dwControlKeyState = (*env)->GetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.controlKeyState); + lpStruct->dwEventFlags = (*env)->GetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.eventFlags); + return lpStruct; +} + +void setMOUSE_EVENT_RECORDFields(JNIEnv *env, jobject lpObject, MOUSE_EVENT_RECORD *lpStruct) +{ + if (!MOUSE_EVENT_RECORDFc.cached) cacheMOUSE_EVENT_RECORDFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, MOUSE_EVENT_RECORDFc.mousePosition); + if (lpObject1 != NULL) setCOORDFields(env, lpObject1, &lpStruct->dwMousePosition); + } + (*env)->SetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.buttonState, (jint)lpStruct->dwButtonState); + (*env)->SetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.controlKeyState, (jint)lpStruct->dwControlKeyState); + (*env)->SetIntField(env, lpObject, MOUSE_EVENT_RECORDFc.eventFlags, (jint)lpStruct->dwEventFlags); +} + +typedef struct SMALL_RECT_FID_CACHE { + int cached; + jclass clazz; + jfieldID left, top, right, bottom; +} SMALL_RECT_FID_CACHE; + +SMALL_RECT_FID_CACHE SMALL_RECTFc; + +void cacheSMALL_RECTFields(JNIEnv *env, jobject lpObject) +{ + if (SMALL_RECTFc.cached) return; + SMALL_RECTFc.clazz = (*env)->GetObjectClass(env, lpObject); + SMALL_RECTFc.left = (*env)->GetFieldID(env, SMALL_RECTFc.clazz, "left", "S"); + SMALL_RECTFc.top = (*env)->GetFieldID(env, SMALL_RECTFc.clazz, "top", "S"); + SMALL_RECTFc.right = (*env)->GetFieldID(env, SMALL_RECTFc.clazz, "right", "S"); + SMALL_RECTFc.bottom = (*env)->GetFieldID(env, SMALL_RECTFc.clazz, "bottom", "S"); + hawtjni_w_barrier(); + SMALL_RECTFc.cached = 1; +} + +SMALL_RECT *getSMALL_RECTFields(JNIEnv *env, jobject lpObject, SMALL_RECT *lpStruct) +{ + if (!SMALL_RECTFc.cached) cacheSMALL_RECTFields(env, lpObject); + lpStruct->Left = (*env)->GetShortField(env, lpObject, SMALL_RECTFc.left); + lpStruct->Top = (*env)->GetShortField(env, lpObject, SMALL_RECTFc.top); + lpStruct->Right = (*env)->GetShortField(env, lpObject, SMALL_RECTFc.right); + lpStruct->Bottom = (*env)->GetShortField(env, lpObject, SMALL_RECTFc.bottom); + return lpStruct; +} + +void setSMALL_RECTFields(JNIEnv *env, jobject lpObject, SMALL_RECT *lpStruct) +{ + if (!SMALL_RECTFc.cached) cacheSMALL_RECTFields(env, lpObject); + (*env)->SetShortField(env, lpObject, SMALL_RECTFc.left, (jshort)lpStruct->Left); + (*env)->SetShortField(env, lpObject, SMALL_RECTFc.top, (jshort)lpStruct->Top); + (*env)->SetShortField(env, lpObject, SMALL_RECTFc.right, (jshort)lpStruct->Right); + (*env)->SetShortField(env, lpObject, SMALL_RECTFc.bottom, (jshort)lpStruct->Bottom); +} + +typedef struct WINDOW_BUFFER_SIZE_RECORD_FID_CACHE { + int cached; + jclass clazz; + jfieldID size; +} WINDOW_BUFFER_SIZE_RECORD_FID_CACHE; + +WINDOW_BUFFER_SIZE_RECORD_FID_CACHE WINDOW_BUFFER_SIZE_RECORDFc; + +void cacheWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject) +{ + if (WINDOW_BUFFER_SIZE_RECORDFc.cached) return; + WINDOW_BUFFER_SIZE_RECORDFc.clazz = (*env)->GetObjectClass(env, lpObject); + WINDOW_BUFFER_SIZE_RECORDFc.size = (*env)->GetFieldID(env, WINDOW_BUFFER_SIZE_RECORDFc.clazz, "size", "Lorg/jline/nativ/Kernel32$COORD;"); + hawtjni_w_barrier(); + WINDOW_BUFFER_SIZE_RECORDFc.cached = 1; +} + +WINDOW_BUFFER_SIZE_RECORD *getWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject, WINDOW_BUFFER_SIZE_RECORD *lpStruct) +{ + if (!WINDOW_BUFFER_SIZE_RECORDFc.cached) cacheWINDOW_BUFFER_SIZE_RECORDFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, WINDOW_BUFFER_SIZE_RECORDFc.size); + if (lpObject1 != NULL) getCOORDFields(env, lpObject1, &lpStruct->dwSize); + } + return lpStruct; +} + +void setWINDOW_BUFFER_SIZE_RECORDFields(JNIEnv *env, jobject lpObject, WINDOW_BUFFER_SIZE_RECORD *lpStruct) +{ + if (!WINDOW_BUFFER_SIZE_RECORDFc.cached) cacheWINDOW_BUFFER_SIZE_RECORDFields(env, lpObject); + { + jobject lpObject1 = (*env)->GetObjectField(env, lpObject, WINDOW_BUFFER_SIZE_RECORDFc.size); + if (lpObject1 != NULL) setCOORDFields(env, lpObject1, &lpStruct->dwSize); + } +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(CloseHandle) + (JNIEnv *env, jclass that, jlong arg0) +{ + jint rc = 0; + rc = (jint)CloseHandle((HANDLE)arg0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(FillConsoleOutputAttribute) + (JNIEnv *env, jclass that, jlong arg0, jshort arg1, jint arg2, jobject arg3, jintArray arg4) +{ + COORD _arg3, *lparg3=NULL; + jint *lparg4=NULL; + jint rc = 0; + if (arg3) if ((lparg3 = getCOORDFields(env, arg3, &_arg3)) == NULL) goto fail; + if (arg4) if ((lparg4 = (*env)->GetIntArrayElements(env, arg4, NULL)) == NULL) goto fail; + rc = (jint)FillConsoleOutputAttribute((HANDLE)(intptr_t)arg0, arg1, arg2, *lparg3, lparg4); +fail: + if (arg4 && lparg4) (*env)->ReleaseIntArrayElements(env, arg4, lparg4, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(FillConsoleOutputCharacterW) + (JNIEnv *env, jclass that, jlong arg0, jchar arg1, jint arg2, jobject arg3, jintArray arg4) +{ + COORD _arg3, *lparg3=NULL; + jint *lparg4=NULL; + jint rc = 0; + if (arg3) if ((lparg3 = getCOORDFields(env, arg3, &_arg3)) == NULL) goto fail; + if (arg4) if ((lparg4 = (*env)->GetIntArrayElements(env, arg4, NULL)) == NULL) goto fail; + rc = (jint)FillConsoleOutputCharacterW((HANDLE)(intptr_t)arg0, arg1, arg2, *lparg3, lparg4); +fail: + if (arg4 && lparg4) (*env)->ReleaseIntArrayElements(env, arg4, lparg4, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(FlushConsoleInputBuffer) + (JNIEnv *env, jclass that, jlong arg0) +{ + jint rc = 0; + rc = (jint)FlushConsoleInputBuffer((HANDLE)(intptr_t)arg0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(FormatMessageW) + (JNIEnv *env, jclass that, jint arg0, jlong arg1, jint arg2, jint arg3, jbyteArray arg4, jint arg5, jlongArray arg6) +{ + jbyte *lparg4=NULL; + jlong *lparg6=NULL; + jint rc = 0; + if (arg4) if ((lparg4 = (*env)->GetPrimitiveArrayCritical(env, arg4, NULL)) == NULL) goto fail; + if (arg6) if ((lparg6 = (*env)->GetPrimitiveArrayCritical(env, arg6, NULL)) == NULL) goto fail; + rc = (jint)FormatMessageW(arg0, (void *)(intptr_t)arg1, arg2, arg3, (void *)lparg4, arg5, (void *)NULL); +fail: + if (arg6 && lparg6) (*env)->ReleasePrimitiveArrayCritical(env, arg6, lparg6, 0); + if (arg4 && lparg4) (*env)->ReleasePrimitiveArrayCritical(env, arg4, lparg4, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(GetConsoleMode) + (JNIEnv *env, jclass that, jlong arg0, jintArray arg1) +{ + jint *lparg1=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = (*env)->GetIntArrayElements(env, arg1, NULL)) == NULL) goto fail; + rc = (jint)GetConsoleMode((HANDLE)(intptr_t)arg0, lparg1); +fail: + if (arg1 && lparg1) (*env)->ReleaseIntArrayElements(env, arg1, lparg1, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(GetConsoleOutputCP) + (JNIEnv *env, jclass that) +{ + jint rc = 0; + rc = (jint)GetConsoleOutputCP(); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(GetConsoleScreenBufferInfo) + (JNIEnv *env, jclass that, jlong arg0, jobject arg1) +{ + CONSOLE_SCREEN_BUFFER_INFO _arg1, *lparg1=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = &_arg1) == NULL) goto fail; + rc = (jint)GetConsoleScreenBufferInfo((HANDLE)(intptr_t)arg0, lparg1); +fail: + if (arg1 && lparg1) setCONSOLE_SCREEN_BUFFER_INFOFields(env, arg1, lparg1); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(GetLastError) + (JNIEnv *env, jclass that) +{ + jint rc = 0; + rc = (jint)GetLastError(); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(GetNumberOfConsoleInputEvents) + (JNIEnv *env, jclass that, jlong arg0, jintArray arg1) +{ + jint *lparg1=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = (*env)->GetIntArrayElements(env, arg1, NULL)) == NULL) goto fail; + rc = (jint)GetNumberOfConsoleInputEvents((HANDLE)(intptr_t)arg0, lparg1); +fail: + if (arg1 && lparg1) (*env)->ReleaseIntArrayElements(env, arg1, lparg1, 0); + return rc; +} + +JNIEXPORT jlong JNICALL Kernel32_NATIVE(GetStdHandle) + (JNIEnv *env, jclass that, jint arg0) +{ + jlong rc = 0; + rc = (intptr_t)(HANDLE)GetStdHandle(arg0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(PeekConsoleInputW) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jint arg2, jintArray arg3) +{ + jint *lparg3=NULL; + jint rc = 0; + if (arg3) if ((lparg3 = (*env)->GetIntArrayElements(env, arg3, NULL)) == NULL) goto fail; + rc = (jint)PeekConsoleInputW((HANDLE)(intptr_t)arg0, (PINPUT_RECORD)(intptr_t)arg1, arg2, lparg3); +fail: + if (arg3 && lparg3) (*env)->ReleaseIntArrayElements(env, arg3, lparg3, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(ReadConsoleInputW) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jint arg2, jintArray arg3) +{ + jint *lparg3=NULL; + jint rc = 0; + if (arg3) if ((lparg3 = (*env)->GetIntArrayElements(env, arg3, NULL)) == NULL) goto fail; + rc = (jint)ReadConsoleInputW((HANDLE)(intptr_t)arg0, (PINPUT_RECORD)(intptr_t)arg1, arg2, lparg3); +fail: + if (arg3 && lparg3) (*env)->ReleaseIntArrayElements(env, arg3, lparg3, 0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(ScrollConsoleScreenBuffer) + (JNIEnv *env, jclass that, jlong arg0, jobject arg1, jobject arg2, jobject arg3, jobject arg4) +{ + SMALL_RECT _arg1, *lparg1=NULL; + SMALL_RECT _arg2, *lparg2=NULL; + COORD _arg3, *lparg3=NULL; + CHAR_INFO _arg4, *lparg4=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = getSMALL_RECTFields(env, arg1, &_arg1)) == NULL) goto fail; + if (arg2) if ((lparg2 = getSMALL_RECTFields(env, arg2, &_arg2)) == NULL) goto fail; + if (arg3) if ((lparg3 = getCOORDFields(env, arg3, &_arg3)) == NULL) goto fail; + if (arg4) if ((lparg4 = getCHAR_INFOFields(env, arg4, &_arg4)) == NULL) goto fail; + rc = (jint)ScrollConsoleScreenBuffer((HANDLE)(intptr_t)arg0, lparg1, lparg2, *lparg3, lparg4); +fail: + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(SetConsoleCursorPosition) + (JNIEnv *env, jclass that, jlong arg0, jobject arg1) +{ + COORD _arg1, *lparg1=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = getCOORDFields(env, arg1, &_arg1)) == NULL) goto fail; + rc = (jint)SetConsoleCursorPosition((HANDLE)(intptr_t)arg0, *lparg1); +fail: + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(SetConsoleMode) + (JNIEnv *env, jclass that, jlong arg0, jint arg1) +{ + return (jint)SetConsoleMode((HANDLE)(intptr_t)arg0, arg1); +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(SetConsoleOutputCP) + (JNIEnv *env, jclass that, jint arg0) +{ + return (jint)SetConsoleOutputCP(arg0); +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(SetConsoleTextAttribute) + (JNIEnv *env, jclass that, jlong arg0, jshort arg1) +{ + return (jint)SetConsoleTextAttribute((HANDLE)arg0, arg1); +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(SetConsoleTitle) + (JNIEnv *env, jclass that, jstring arg0) +{ + const jchar *lparg0= NULL; + jint rc = 0; + if (arg0) if ((lparg0 = (*env)->GetStringChars(env, arg0, NULL)) == NULL) goto fail; + rc = (jint)SetConsoleTitle(lparg0); +fail: + if (arg0 && lparg0) (*env)->ReleaseStringChars(env, arg0, lparg0); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(WaitForSingleObject) + (JNIEnv *env, jclass that, jlong arg0, jint arg1) +{ + return (jint)WaitForSingleObject((HANDLE)arg0, arg1); +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(WriteConsoleW) + (JNIEnv *env, jclass that, jlong arg0, jcharArray arg1, jint arg2, jintArray arg3, jlong arg4) +{ + jchar *lparg1=NULL; + jint *lparg3=NULL; + jint rc = 0; + if (arg1) if ((lparg1 = (*env)->GetCharArrayElements(env, arg1, NULL)) == NULL) goto fail; + if (arg3) if ((lparg3 = (*env)->GetIntArrayElements(env, arg3, NULL)) == NULL) goto fail; + rc = (jint)WriteConsoleW((HANDLE)(intptr_t)arg0, lparg1, arg2, lparg3, (LPVOID)(intptr_t)arg4); +fail: + if (arg3 && lparg3) (*env)->ReleaseIntArrayElements(env, arg3, lparg3, 0); + if (arg1 && lparg1) (*env)->ReleaseCharArrayElements(env, arg1, lparg1, JNI_ABORT); + return rc; +} + +JNIEXPORT jint JNICALL Kernel32_NATIVE(_1getch) + (JNIEnv *env, jclass that) +{ + jint rc = 0; + rc = (jint)_getch(); + return rc; +} + +JNIEXPORT void JNICALL Kernel32_NATIVE(free) + (JNIEnv *env, jclass that, jlong arg0) +{ + free((void *)(intptr_t)arg0); +} + +JNIEXPORT void JNICALL Kernel32_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "FOREGROUND_BLUE", "S"), (jshort)FOREGROUND_BLUE); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "FOREGROUND_GREEN", "S"), (jshort)FOREGROUND_GREEN); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "FOREGROUND_RED", "S"), (jshort)FOREGROUND_RED); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "FOREGROUND_INTENSITY", "S"), (jshort)FOREGROUND_INTENSITY); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "BACKGROUND_BLUE", "S"), (jshort)BACKGROUND_BLUE); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "BACKGROUND_GREEN", "S"), (jshort)BACKGROUND_GREEN); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "BACKGROUND_RED", "S"), (jshort)BACKGROUND_RED); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "BACKGROUND_INTENSITY", "S"), (jshort)BACKGROUND_INTENSITY); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_LEADING_BYTE", "S"), (jshort)COMMON_LVB_LEADING_BYTE); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_TRAILING_BYTE", "S"), (jshort)COMMON_LVB_TRAILING_BYTE); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_GRID_HORIZONTAL", "S"), (jshort)COMMON_LVB_GRID_HORIZONTAL); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_GRID_LVERTICAL", "S"), (jshort)COMMON_LVB_GRID_LVERTICAL); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_GRID_RVERTICAL", "S"), (jshort)COMMON_LVB_GRID_RVERTICAL); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_REVERSE_VIDEO", "S"), (jshort)COMMON_LVB_REVERSE_VIDEO); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "COMMON_LVB_UNDERSCORE", "S"), (jshort)COMMON_LVB_UNDERSCORE); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "FORMAT_MESSAGE_FROM_SYSTEM", "I"), (jint)FORMAT_MESSAGE_FROM_SYSTEM); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "STD_INPUT_HANDLE", "I"), (jint)STD_INPUT_HANDLE); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "STD_OUTPUT_HANDLE", "I"), (jint)STD_OUTPUT_HANDLE); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "STD_ERROR_HANDLE", "I"), (jint)STD_ERROR_HANDLE); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "INVALID_HANDLE_VALUE", "I"), (jint)INVALID_HANDLE_VALUE); + return; +} + +JNIEXPORT jlong JNICALL Kernel32_NATIVE(malloc) + (JNIEnv *env, jclass that, jlong arg0) +{ + jlong rc = 0; + rc = (intptr_t)(void *)malloc((size_t)arg0); + return rc; +} + +JNIEXPORT void JNICALL CHAR_INFO_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(CHAR_INFO)); + return; +} + +JNIEXPORT void JNICALL CONSOLE_SCREEN_BUFFER_INFO_NATIVE(init)(JNIEnv *env, jclass that) +{ +#if defined(_WIN32) || defined(_WIN64) + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(CONSOLE_SCREEN_BUFFER_INFO)); +#endif + return; +} + +JNIEXPORT void JNICALL COORD_NATIVE(init)(JNIEnv *env, jclass that) +{ +#if defined(_WIN32) || defined(_WIN64) + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(COORD)); +#endif + return; +} + +JNIEXPORT void JNICALL FOCUS_EVENT_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ +#if defined(_WIN32) || defined(_WIN64) + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(WINDOW_BUFFER_SIZE_RECORD)); +#endif + return; +} + +JNIEXPORT void JNICALL INPUT_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(INPUT_RECORD)); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "KEY_EVENT", "S"), (jshort)KEY_EVENT); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "MOUSE_EVENT", "S"), (jshort)MOUSE_EVENT); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "WINDOW_BUFFER_SIZE_EVENT", "S"), (jshort)WINDOW_BUFFER_SIZE_EVENT); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "FOCUS_EVENT", "S"), (jshort)FOCUS_EVENT); + (*env)->SetStaticShortField(env, that, (*env)->GetStaticFieldID(env, that, "MENU_EVENT", "S"), (jshort)MENU_EVENT); + return; +} +JNIEXPORT void JNICALL INPUT_RECORD_NATIVE(memmove) + (JNIEnv *env, jclass that, jobject arg0, jlong arg1, jlong arg2) +{ + INPUT_RECORD _arg0, *lparg0=NULL; + if (arg0) if ((lparg0 = &_arg0) == NULL) goto fail; + memmove((void *)lparg0, (const void *)(intptr_t)arg1, (size_t)arg2); +fail: + if (arg0 && lparg0) setINPUT_RECORDFields(env, arg0, lparg0); +} + +JNIEXPORT void JNICALL KEY_EVENT_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(KEY_EVENT_RECORD)); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "CAPSLOCK_ON", "I"), (jint)CAPSLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "NUMLOCK_ON", "I"), (jint)NUMLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SCROLLLOCK_ON", "I"), (jint)SCROLLLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "ENHANCED_KEY", "I"), (jint)ENHANCED_KEY); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "LEFT_ALT_PRESSED", "I"), (jint)LEFT_ALT_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "LEFT_CTRL_PRESSED", "I"), (jint)LEFT_CTRL_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "RIGHT_ALT_PRESSED", "I"), (jint)RIGHT_ALT_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "RIGHT_CTRL_PRESSED", "I"), (jint)RIGHT_CTRL_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SHIFT_PRESSED", "I"), (jint)SHIFT_PRESSED); + return; +} + +JNIEXPORT void JNICALL MENU_EVENT_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(MENU_EVENT_RECORD)); + return; +} + +JNIEXPORT void JNICALL MOUSE_EVENT_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(MOUSE_EVENT_RECORD)); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "FROM_LEFT_1ST_BUTTON_PRESSED", "I"), (jint)FROM_LEFT_1ST_BUTTON_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "FROM_LEFT_2ND_BUTTON_PRESSED", "I"), (jint)FROM_LEFT_2ND_BUTTON_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "FROM_LEFT_3RD_BUTTON_PRESSED", "I"), (jint)FROM_LEFT_3RD_BUTTON_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "FROM_LEFT_4TH_BUTTON_PRESSED", "I"), (jint)FROM_LEFT_4TH_BUTTON_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "RIGHTMOST_BUTTON_PRESSED", "I"), (jint)RIGHTMOST_BUTTON_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "CAPSLOCK_ON", "I"), (jint)CAPSLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "NUMLOCK_ON", "I"), (jint)NUMLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SCROLLLOCK_ON", "I"), (jint)SCROLLLOCK_ON); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "ENHANCED_KEY", "I"), (jint)ENHANCED_KEY); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "LEFT_ALT_PRESSED", "I"), (jint)LEFT_ALT_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "LEFT_CTRL_PRESSED", "I"), (jint)LEFT_CTRL_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "RIGHT_ALT_PRESSED", "I"), (jint)RIGHT_ALT_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "RIGHT_CTRL_PRESSED", "I"), (jint)RIGHT_CTRL_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SHIFT_PRESSED", "I"), (jint)SHIFT_PRESSED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "DOUBLE_CLICK", "I"), (jint)DOUBLE_CLICK); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "MOUSE_HWHEELED", "I"), (jint)MOUSE_HWHEELED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "MOUSE_MOVED", "I"), (jint)MOUSE_MOVED); + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "MOUSE_WHEELED", "I"), (jint)MOUSE_WHEELED); + return; +} + +JNIEXPORT void JNICALL SMALL_RECT_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(SMALL_RECT)); + return; +} + +JNIEXPORT void JNICALL WINDOW_BUFFER_SIZE_RECORD_NATIVE(init)(JNIEnv *env, jclass that) +{ + (*env)->SetStaticIntField(env, that, (*env)->GetStaticFieldID(env, that, "SIZEOF", "I"), (jint)sizeof(WINDOW_BUFFER_SIZE_RECORD)); + return; +} + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _OBJECT_NAME_INFORMATION { + UNICODE_STRING Name; + WCHAR NameBuffer[0]; +} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; + +typedef enum { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectAllInformation, + ObjectDataInformation +} OBJECT_INFORMATION_CLASS; + +typedef NTSTATUS (NTAPI *TFNNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG); +TFNNtQueryObject NtQueryObject = 0; + +HANDLE hModuleNtDll = 0; + +JNIEXPORT jint JNICALL Kernel32_NATIVE(isatty) + (JNIEnv *env, jclass that, jint arg0) +{ + jint rc; + + ULONG result; + BYTE buffer[1024]; + POBJECT_NAME_INFORMATION nameinfo = (POBJECT_NAME_INFORMATION) buffer; + PWSTR name; + DWORD mode; + + /* check if fd is a pipe */ + HANDLE h = (HANDLE) _get_osfhandle(arg0); + DWORD t = h != NULL ? GetFileType(h) : 0; + if (h != NULL && t == FILE_TYPE_CHAR) { + // check that this is a real tty because the /dev/null + // and /dev/zero streams are also of type FILE_TYPE_CHAR + rc = GetConsoleMode(h, &mode) != 0; + } + else { + if (hModuleNtDll == 0) { + hModuleNtDll = LoadLibraryW(L"ntdll.dll"); + } + if (hModuleNtDll == 0) { + rc = 0; + } + else { + if (NtQueryObject == 0) { + NtQueryObject = (TFNNtQueryObject) GetProcAddress(hModuleNtDll, "NtQueryObject"); + } + if (NtQueryObject == 0) { + rc = 0; + } + /* get pipe name */ + else if (NtQueryObject(h, ObjectNameInformation, buffer, sizeof(buffer) - 2, &result) != 0) { + rc = 0; + } + else { + + name = nameinfo->Name.Buffer; + if (name == NULL) { + rc = 0; + } + else { + name[nameinfo->Name.Length / 2] = 0; + + //fprintf( stderr, "Standard stream %d: pipe name: %S\n", arg0, name); + + /* + * Check if this could be a MSYS2 pty pipe ('msys-XXXX-ptyN-XX') + * or a cygwin pty pipe ('cygwin-XXXX-ptyN-XX') + */ + if ((wcsstr(name, L"msys-") || wcsstr(name, L"cygwin-")) && wcsstr(name, L"-pty")) { + rc = 1; + } else { + // This is definitely not a tty + rc = 0; + } + } + } + } + } + + return rc; +} + +#endif diff --git a/native/src/main/resources/org/jline/nativ/FreeBSD/x86/libjlinenative.so b/native/src/main/resources/org/jline/nativ/FreeBSD/x86/libjlinenative.so index 9ad59f06b..13258b54e 100755 Binary files a/native/src/main/resources/org/jline/nativ/FreeBSD/x86/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/FreeBSD/x86/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/FreeBSD/x86_64/libjlinenative.so b/native/src/main/resources/org/jline/nativ/FreeBSD/x86_64/libjlinenative.so index db84a0835..fa5064632 100755 Binary files a/native/src/main/resources/org/jline/nativ/FreeBSD/x86_64/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/FreeBSD/x86_64/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/arm/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/arm/libjlinenative.so index f67f1e324..6791f478d 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/arm/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/arm/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/arm64/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/arm64/libjlinenative.so index f6b05768f..de656faad 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/arm64/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/arm64/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/armv6/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/armv6/libjlinenative.so index 189d638e4..bfe352076 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/armv6/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/armv6/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/armv7/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/armv7/libjlinenative.so index 841b5e54f..d546f10ca 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/armv7/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/armv7/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/ppc64/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/ppc64/libjlinenative.so index 9c06e44d0..e79106572 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/ppc64/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/ppc64/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/x86/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/x86/libjlinenative.so index ff8aef34d..932849e3d 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/x86/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/x86/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Linux/x86_64/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Linux/x86_64/libjlinenative.so index 918a45b3b..5772a2538 100755 Binary files a/native/src/main/resources/org/jline/nativ/Linux/x86_64/libjlinenative.so and b/native/src/main/resources/org/jline/nativ/Linux/x86_64/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Mac/arm64/libjlinenative.jnilib b/native/src/main/resources/org/jline/nativ/Mac/arm64/libjlinenative.jnilib index b968d5e5a..d30f8cbb1 100755 Binary files a/native/src/main/resources/org/jline/nativ/Mac/arm64/libjlinenative.jnilib and b/native/src/main/resources/org/jline/nativ/Mac/arm64/libjlinenative.jnilib differ diff --git a/native/src/main/resources/org/jline/nativ/Mac/x86/libjlinenative.jnilib b/native/src/main/resources/org/jline/nativ/Mac/x86/libjlinenative.jnilib index e1c953ffa..3418272cd 100755 Binary files a/native/src/main/resources/org/jline/nativ/Mac/x86/libjlinenative.jnilib and b/native/src/main/resources/org/jline/nativ/Mac/x86/libjlinenative.jnilib differ diff --git a/native/src/main/resources/org/jline/nativ/Mac/x86_64/libjlinenative.jnilib b/native/src/main/resources/org/jline/nativ/Mac/x86_64/libjlinenative.jnilib index d7f243142..e82521301 100755 Binary files a/native/src/main/resources/org/jline/nativ/Mac/x86_64/libjlinenative.jnilib and b/native/src/main/resources/org/jline/nativ/Mac/x86_64/libjlinenative.jnilib differ diff --git a/native/src/main/resources/org/jline/nativ/Windows/arm64/libjlinenative.so b/native/src/main/resources/org/jline/nativ/Windows/arm64/libjlinenative.so new file mode 100755 index 000000000..3fa5ec086 Binary files /dev/null and b/native/src/main/resources/org/jline/nativ/Windows/arm64/libjlinenative.so differ diff --git a/native/src/main/resources/org/jline/nativ/Windows/x86/jlinenative.dll b/native/src/main/resources/org/jline/nativ/Windows/x86/jlinenative.dll index 10ddc9c48..f6bfa9ee6 100755 Binary files a/native/src/main/resources/org/jline/nativ/Windows/x86/jlinenative.dll and b/native/src/main/resources/org/jline/nativ/Windows/x86/jlinenative.dll differ diff --git a/native/src/main/resources/org/jline/nativ/Windows/x86_64/jlinenative.dll b/native/src/main/resources/org/jline/nativ/Windows/x86_64/jlinenative.dll index 28e694811..6b252cf1e 100755 Binary files a/native/src/main/resources/org/jline/nativ/Windows/x86_64/jlinenative.dll and b/native/src/main/resources/org/jline/nativ/Windows/x86_64/jlinenative.dll differ diff --git a/pom.xml b/pom.xml index 6e1e237ce..f8c6af67a 100644 --- a/pom.xml +++ b/pom.xml @@ -61,6 +61,7 @@ terminal-ffm terminal-jna terminal-jansi + terminal-jni reader builtins console @@ -144,6 +145,12 @@ ${project.version} + + org.jline + jline-terminal-jni + ${project.version} + + org.jline jline-terminal-jna diff --git a/reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java b/reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java index 7d2e28557..76a069a24 100644 --- a/reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java +++ b/reader/src/test/java/org/jline/terminal/impl/ExternalTerminalTest.java @@ -61,19 +61,6 @@ public void testInput() throws IOException, InterruptedException { testConsole(outIn, out, console); } - /* SANDBOX JANSI - @Test - public void testPosix() throws IOException, InterruptedException { - PipedInputStream in = new PipedInputStream(); - PipedOutputStream outIn = new PipedOutputStream(in); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - Console terminal = new PosixPtyConsole("ansi", new ConsoleReaderBuilder(), NativePty.open(null, null), in, out, "UTF-8"); - - testConsole(outIn, out, terminal); - } - */ - private void testConsole(PipedOutputStream outIn, ByteArrayOutputStream out, Terminal terminal) throws IOException, InterruptedException { Attributes attributes = terminal.getAttributes(); diff --git a/terminal-jni/pom.xml b/terminal-jni/pom.xml new file mode 100644 index 000000000..94183a86e --- /dev/null +++ b/terminal-jni/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + + org.jline + jline-parent + 3.23.1-SNAPSHOT + + + jline-terminal-jni + JLine JNI Terminal + + + org.jline.terminal.jni + + + + + org.jline + jline-native + + + + org.jline + jline-terminal + + + + org.junit.jupiter + junit-jupiter-api + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + *;-noimport:=true + org.jline.terminal + + + + + + + diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniNativePty.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniNativePty.java new file mode 100644 index 000000000..a6faaa89e --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniNativePty.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2002-2020, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni; + +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 org.jline.nativ.CLibrary; +import org.jline.nativ.Kernel32; +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.impl.AbstractPty; +import org.jline.terminal.impl.jni.win.NativeWinSysTerminal; +import org.jline.terminal.spi.Pty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; +import org.jline.utils.OSUtils; + +import static org.jline.nativ.CLibrary.TCSANOW; + +public abstract class JniNativePty extends AbstractPty implements Pty { + + 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 JniNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + String name) { + this(provider, systemStream, master, masterFD, slave, slaveFD, slave, slaveFD, name); + } + + public JniNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + int slaveOut, + FileDescriptor slaveOutFD, + String name) { + super(provider, systemStream); + this.master = master; + this.slave = slave; + this.slaveOut = slaveOut; + this.name = name; + this.masterFD = masterFD; + this.slaveFD = slaveFD; + this.slaveOutFD = slaveOutFD; + } + + protected static String ttyname(int fd) throws IOException { + String name = CLibrary.ttyname(fd); + if (name != null) { + name = name.trim(); + } + if (name == null || name.isEmpty()) { + throw new IOException("Not a tty"); + } + return name; + } + + @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 { + CLibrary.Termios tios = new CLibrary.Termios(); + CLibrary.tcgetattr(slave, tios); + return toAttributes(tios); + } + + @Override + protected void doSetAttr(Attributes attr) throws IOException { + CLibrary.Termios tios = toTermios(attr); + CLibrary.tcsetattr(slave, TCSANOW, tios); + } + + @Override + public Size getSize() throws IOException { + CLibrary.WinSize sz = new CLibrary.WinSize(); + int res = CLibrary.ioctl(slave, CLibrary.TIOCGWINSZ, sz); + if (res != 0) { + throw new IOException("Error calling ioctl(TIOCGWINSZ): return code is " + res); + } + return new Size(sz.ws_col, sz.ws_row); + } + + @Override + public void setSize(Size size) throws IOException { + CLibrary.WinSize sz = new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()); + int res = CLibrary.ioctl(slave, CLibrary.TIOCSWINSZ, sz); + if (res != 0) { + throw new IOException("Error calling ioctl(TIOCSWINSZ): return code is " + res); + } + } + + protected abstract CLibrary.Termios toTermios(Attributes t); + + protected abstract Attributes toAttributes(CLibrary.Termios tios); + + @Override + public String toString() { + return "NativePty[" + getName() + "]"; + } + + public static boolean isPosixSystemStream(SystemStream stream) { + return CLibrary.isatty(fd(stream)) == 1; + } + + public static String posixSystemStreamName(SystemStream systemStream) { + return CLibrary.ttyname(fd(systemStream)); + } + + public static int systemStreamWidth(SystemStream systemStream) { + try { + if (OSUtils.IS_WINDOWS) { + Kernel32.CONSOLE_SCREEN_BUFFER_INFO info = new Kernel32.CONSOLE_SCREEN_BUFFER_INFO(); + long outConsole = NativeWinSysTerminal.getConsole(systemStream); + Kernel32.GetConsoleScreenBufferInfo(outConsole, info); + return info.windowWidth(); + } else { + CLibrary.WinSize sz = new CLibrary.WinSize(); + int res = CLibrary.ioctl(fd(systemStream), CLibrary.TIOCGWINSZ, sz); + if (res != 0) { + throw new IOException("Error calling ioctl(TIOCGWINSZ): return code is " + res); + } + return sz.ws_col; + } + } catch (Throwable t) { + return -1; + } + } + + private static int fd(SystemStream systemStream) { + switch (systemStream) { + case Input: + return 0; + case Output: + return 1; + case Error: + return 2; + default: + return -1; + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniTerminalProvider.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniTerminalProvider.java new file mode 100644 index 000000000..bc59fa332 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniTerminalProvider.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2002-2020, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.Terminal; +import org.jline.terminal.impl.PosixPtyTerminal; +import org.jline.terminal.impl.PosixSysTerminal; +import org.jline.terminal.impl.jni.freebsd.FreeBsdNativePty; +import org.jline.terminal.impl.jni.linux.LinuxNativePty; +import org.jline.terminal.impl.jni.osx.OsXNativePty; +import org.jline.terminal.impl.jni.solaris.SolarisNativePty; +import org.jline.terminal.impl.jni.win.NativeWinSysTerminal; +import org.jline.terminal.spi.Pty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; +import org.jline.utils.OSUtils; + +public class JniTerminalProvider implements TerminalProvider { + + @Override + public String name() { + return "native"; + } + + public Pty current(SystemStream systemStream) throws IOException { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Linux")) { + return LinuxNativePty.current(this, systemStream); + } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { + return OsXNativePty.current(this, systemStream); + } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { + return SolarisNativePty.current(this, systemStream); + } else if (osName.startsWith("FreeBSD")) { + return FreeBsdNativePty.current(this, systemStream); + } + throw new UnsupportedOperationException(); + } + + public Pty open(Attributes attributes, Size size) throws IOException { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Linux")) { + return LinuxNativePty.open(this, attributes, size); + } else if (osName.startsWith("Mac") || osName.startsWith("Darwin")) { + return OsXNativePty.open(this, attributes, size); + } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { + return SolarisNativePty.open(this, attributes, size); + } else if (osName.startsWith("FreeBSD")) { + return FreeBsdNativePty.open(this, attributes, size); + } + throw new UnsupportedOperationException(); + } + + @Override + public Terminal sysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream) + throws IOException { + if (OSUtils.IS_WINDOWS) { + return winSysTerminal( + name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, systemStream); + } else { + return posixSysTerminal( + name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused, systemStream); + } + } + + public Terminal winSysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream) + throws IOException { + return NativeWinSysTerminal.createTerminal( + this, systemStream, name, type, ansiPassThrough, encoding, nativeSignals, signalHandler, paused); + } + + public Terminal posixSysTerminal( + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + Terminal.SignalHandler signalHandler, + boolean paused, + SystemStream systemStream) + throws IOException { + Pty pty = current(systemStream); + return new PosixSysTerminal(name, type, pty, encoding, nativeSignals, signalHandler); + } + + @Override + public Terminal newTerminal( + String name, + String type, + InputStream in, + OutputStream out, + Charset encoding, + Terminal.SignalHandler signalHandler, + boolean paused, + Attributes attributes, + Size size) + throws IOException { + Pty pty = open(attributes, size); + return new PosixPtyTerminal(name, type, pty, in, out, encoding, signalHandler, paused); + } + + @Override + public boolean isSystemStream(SystemStream stream) { + try { + if (OSUtils.IS_WINDOWS) { + return isWindowsSystemStream(stream); + } else { + return isPosixSystemStream(stream); + } + } catch (Throwable t) { + return false; + } + } + + public boolean isWindowsSystemStream(SystemStream stream) { + return NativeWinSysTerminal.isWindowsSystemStream(stream); + } + + public boolean isPosixSystemStream(SystemStream stream) { + return JniNativePty.isPosixSystemStream(stream); + } + + @Override + public String systemStreamName(SystemStream stream) { + return JniNativePty.posixSystemStreamName(stream); + } + + @Override + public int systemStreamWidth(SystemStream stream) { + return JniNativePty.systemStreamWidth(stream); + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/freebsd/FreeBsdNativePty.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/freebsd/FreeBsdNativePty.java new file mode 100644 index 000000000..8708599b1 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/freebsd/FreeBsdNativePty.java @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2002-2017, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.freebsd; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.EnumMap; +import java.util.EnumSet; + +import org.jline.nativ.CLibrary; +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.impl.jni.JniNativePty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; + +public class FreeBsdNativePty extends JniNativePty { + + public static FreeBsdNativePty current(TerminalProvider provider, SystemStream systemStream) throws IOException { + try { + switch (systemStream) { + case Output: + return new FreeBsdNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 1, FileDescriptor.out, ttyname(1)); + case Error: + return new FreeBsdNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 2, FileDescriptor.err, ttyname(2)); + default: + throw new IllegalArgumentException("Unsupport stream for console: " + systemStream); + } + } catch (IOException e) { + throw new IOException("Not a tty", e); + } + } + + public static FreeBsdNativePty open(TerminalProvider provider, Attributes attr, Size size) throws IOException { + int[] master = new int[1]; + int[] slave = new int[1]; + byte[] buf = new byte[64]; + CLibrary.openpty( + master, + slave, + buf, + attr != null ? termios(attr) : null, + size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); + int len = 0; + while (buf[len] != 0) { + len++; + } + String name = new String(buf, 0, len); + return new FreeBsdNativePty( + provider, null, master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); + } + + public FreeBsdNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, name); + } + + public FreeBsdNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + int slaveOut, + FileDescriptor slaveOutFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, slaveOut, slaveOutFD, name); + } + // CONSTANTS + + private static final int VEOF = 0; + private static final int VEOL = 1; + private static final int VEOL2 = 2; + private static final int VERASE = 3; + private static final int VWERASE = 4; + private static final int VKILL = 5; + private static final int VREPRINT = 6; + private static final int VERASE2 = 7; + private static final int VINTR = 8; + private static final int VQUIT = 9; + private static final int VSUSP = 10; + private static final int VDSUSP = 11; + private static final int VSTART = 12; + private static final int VSTOP = 13; + private static final int VLNEXT = 14; + private static final int VDISCARD = 15; + private static final int VMIN = 16; + private static final int VTIME = 17; + private static final int VSTATUS = 18; + + private static final int IGNBRK = 0x0000001; + private static final int BRKINT = 0x0000002; + private static final int IGNPAR = 0x0000004; + private static final int PARMRK = 0x0000008; + private static final int INPCK = 0x0000010; + private static final int ISTRIP = 0x0000020; + private static final int INLCR = 0x0000040; + private static final int IGNCR = 0x0000080; + private static final int ICRNL = 0x0000100; + private static final int IXON = 0x0000200; + private static final int IXOFF = 0x0000400; + private static final int IXANY = 0x0000800; + private static final int IMAXBEL = 0x0002000; + + private static final int OPOST = 0x0000001; + private static final int ONLCR = 0x0000002; + private static final int TABDLY = 0x0000004; + private static final int TAB0 = 0x0000000; + private static final int TAB3 = 0x0000004; + private static final int ONOEOT = 0x0000008; + private static final int OCRNL = 0x0000010; + private static final int ONLRET = 0x0000040; + + private static final int CIGNORE = 0x0000001; + private static final int CSIZE = 0x0000300; + private static final int CS5 = 0x0000000; + private static final int CS6 = 0x0000100; + private static final int CS7 = 0x0000200; + private static final int CS8 = 0x0000300; + private static final int CSTOPB = 0x0000400; + private static final int CREAD = 0x0000800; + private static final int PARENB = 0x0001000; + private static final int PARODD = 0x0002000; + private static final int HUPCL = 0x0004000; + private static final int CLOCAL = 0x0008000; + + private static final int ECHOKE = 0x0000001; + private static final int ECHOE = 0x0000002; + private static final int ECHOK = 0x0000004; + private static final int ECHO = 0x0000008; + private static final int ECHONL = 0x0000010; + private static final int ECHOPRT = 0x0000020; + private static final int ECHOCTL = 0x0000040; + private static final int ISIG = 0x0000080; + private static final int ICANON = 0x0000100; + private static final int ALTWERASE = 0x000200; + private static final int IEXTEN = 0x0000400; + private static final int EXTPROC = 0x0000800; + private static final int TOSTOP = 0x0400000; + private static final int FLUSHO = 0x0800000; + private static final int PENDIN = 0x2000000; + private static final int NOFLSH = 0x8000000; + + protected CLibrary.Termios toTermios(Attributes t) { + return termios(t); + } + + static CLibrary.Termios termios(Attributes t) { + CLibrary.Termios tio = new CLibrary.Termios(); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); + // tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); + // Output flags + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); + // Control flags + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); + // Local flags + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); + // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); + // Control chars + tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); + tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); + tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); + tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); + tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); + tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); + tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); + tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); + tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); + tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); + // tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); + tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); + tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); + tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); + tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); + tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); + tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); + // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); + return tio; + } + + protected Attributes toAttributes(CLibrary.Termios tio) { + Attributes attr = new Attributes(); + // Input flags + EnumSet iflag = attr.getInputFlags(); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); + // addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); + // Output flags + EnumSet oflag = attr.getOutputFlags(); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); + // Control flags + EnumSet cflag = attr.getControlFlags(); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); + // Local flags + EnumSet lflag = attr.getLocalFlags(); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); + // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); + // Control chars + EnumMap cc = attr.getControlChars(); + cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); + cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); + cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); + cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); + cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); + cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); + cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); + cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); + cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); + cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); + // cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); + cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); + cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); + cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); + cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); + cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); + cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); + // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); + // Return + return attr; + } + + 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); + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/linux/LinuxNativePty.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/linux/LinuxNativePty.java new file mode 100644 index 000000000..537fca079 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/linux/LinuxNativePty.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2002-2017, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.linux; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.EnumMap; +import java.util.EnumSet; + +import org.jline.nativ.CLibrary; +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.impl.jni.JniNativePty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; + +public class LinuxNativePty extends JniNativePty { + + public static LinuxNativePty current(TerminalProvider provider, SystemStream systemStream) throws IOException { + try { + switch (systemStream) { + case Output: + return new LinuxNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 1, FileDescriptor.out, ttyname(1)); + case Error: + return new LinuxNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 2, FileDescriptor.err, ttyname(2)); + default: + throw new IllegalArgumentException("Unsupport stream for console: " + systemStream); + } + } catch (IOException e) { + throw new IOException("Not a tty", e); + } + } + + public static LinuxNativePty open(TerminalProvider provider, Attributes attr, Size size) throws IOException { + int[] master = new int[1]; + int[] slave = new int[1]; + byte[] buf = new byte[64]; + CLibrary.openpty( + master, + slave, + buf, + attr != null ? termios(attr) : null, + size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); + int len = 0; + while (buf[len] != 0) { + len++; + } + String name = new String(buf, 0, len); + return new LinuxNativePty( + provider, + null, + master[0], + newDescriptor(master[0]), + slave[0], + newDescriptor(slave[0]), + 2, + FileDescriptor.err, + name); + } + + public LinuxNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + int slaveOut, + FileDescriptor slaveOutFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, slaveOut, slaveOutFD, name); + } + // CONSTANTS + + private static final int VINTR = 0; + private static final int VQUIT = 1; + private static final int VERASE = 2; + private static final int VKILL = 3; + private static final int VEOF = 4; + private static final int VTIME = 5; + private static final int VMIN = 6; + private static final int VSWTC = 7; + private static final int VSTART = 8; + private static final int VSTOP = 9; + private static final int VSUSP = 10; + private static final int VEOL = 11; + private static final int VREPRINT = 12; + private static final int VDISCARD = 13; + private static final int VWERASE = 14; + private static final int VLNEXT = 15; + private static final int VEOL2 = 16; + + private static final int IGNBRK = 0x0000001; + private static final int BRKINT = 0x0000002; + private static final int IGNPAR = 0x0000004; + private static final int PARMRK = 0x0000008; + private static final int INPCK = 0x0000010; + private static final int ISTRIP = 0x0000020; + private static final int INLCR = 0x0000040; + private static final int IGNCR = 0x0000080; + private static final int ICRNL = 0x0000100; + private static final int IUCLC = 0x0000200; + private static final int IXON = 0x0000400; + private static final int IXANY = 0x0000800; + private static final int IXOFF = 0x0001000; + private static final int IMAXBEL = 0x0002000; + private static final int IUTF8 = 0x0004000; + + private static final int OPOST = 0x0000001; + private static final int OLCUC = 0x0000002; + private static final int ONLCR = 0x0000004; + private static final int OCRNL = 0x0000008; + private static final int ONOCR = 0x0000010; + private static final int ONLRET = 0x0000020; + private static final int OFILL = 0x0000040; + private static final int OFDEL = 0x0000080; + private static final int NLDLY = 0x0000100; + private static final int NL0 = 0x0000000; + private static final int NL1 = 0x0000100; + private static final int CRDLY = 0x0000600; + private static final int CR0 = 0x0000000; + private static final int CR1 = 0x0000200; + private static final int CR2 = 0x0000400; + private static final int CR3 = 0x0000600; + private static final int TABDLY = 0x0001800; + private static final int TAB0 = 0x0000000; + private static final int TAB1 = 0x0000800; + private static final int TAB2 = 0x0001000; + private static final int TAB3 = 0x0001800; + private static final int XTABS = 0x0001800; + private static final int BSDLY = 0x0002000; + private static final int BS0 = 0x0000000; + private static final int BS1 = 0x0002000; + private static final int VTDLY = 0x0004000; + private static final int VT0 = 0x0000000; + private static final int VT1 = 0x0004000; + private static final int FFDLY = 0x0008000; + private static final int FF0 = 0x0000000; + private static final int FF1 = 0x0008000; + + private static final int CBAUD = 0x000100f; + private static final int B0 = 0x0000000; + private static final int B50 = 0x0000001; + private static final int B75 = 0x0000002; + private static final int B110 = 0x0000003; + private static final int B134 = 0x0000004; + private static final int B150 = 0x0000005; + private static final int B200 = 0x0000006; + private static final int B300 = 0x0000007; + private static final int B600 = 0x0000008; + private static final int B1200 = 0x0000009; + private static final int B1800 = 0x000000a; + private static final int B2400 = 0x000000b; + private static final int B4800 = 0x000000c; + private static final int B9600 = 0x000000d; + private static final int B19200 = 0x000000e; + private static final int B38400 = 0x000000f; + private static final int EXTA = B19200; + private static final int EXTB = B38400; + private static final int CSIZE = 0x0000030; + private static final int CS5 = 0x0000000; + private static final int CS6 = 0x0000010; + private static final int CS7 = 0x0000020; + private static final int CS8 = 0x0000030; + private static final int CSTOPB = 0x0000040; + private static final int CREAD = 0x0000080; + private static final int PARENB = 0x0000100; + private static final int PARODD = 0x0000200; + private static final int HUPCL = 0x0000400; + private static final int CLOCAL = 0x0000800; + + private static final int ISIG = 0x0000001; + private static final int ICANON = 0x0000002; + private static final int XCASE = 0x0000004; + private static final int ECHO = 0x0000008; + private static final int ECHOE = 0x0000010; + private static final int ECHOK = 0x0000020; + private static final int ECHONL = 0x0000040; + private static final int NOFLSH = 0x0000080; + private static final int TOSTOP = 0x0000100; + private static final int ECHOCTL = 0x0000200; + private static final int ECHOPRT = 0x0000400; + private static final int ECHOKE = 0x0000800; + private static final int FLUSHO = 0x0001000; + private static final int PENDIN = 0x0002000; + private static final int IEXTEN = 0x0008000; + private static final int EXTPROC = 0x0010000; + + protected CLibrary.Termios toTermios(Attributes t) { + return termios(t); + } + + static CLibrary.Termios termios(Attributes t) { + CLibrary.Termios tio = new CLibrary.Termios(); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); + // Output flags + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); + // Control flags + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); + // Local flags + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); + // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); + // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); + // Control chars + tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); + tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); + tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); + tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); + tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); + tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); + tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); + tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); + tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); + tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); + tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); + tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); + tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); + tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); + tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); + tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); + // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); + return tio; + } + + protected Attributes toAttributes(CLibrary.Termios tio) { + Attributes attr = new Attributes(); + // Input flags + EnumSet iflag = attr.getInputFlags(); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); + // Output flags + EnumSet oflag = attr.getOutputFlags(); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); + // Control flags + EnumSet cflag = attr.getControlFlags(); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); + // Local flags + EnumSet lflag = attr.getLocalFlags(); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); + // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); + // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); + // Control chars + EnumMap cc = attr.getControlChars(); + cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); + cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); + cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); + cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); + cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); + cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); + cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); + cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); + cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); + cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); + cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); + cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); + cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); + cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); + cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); + cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); + // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); + // Return + return attr; + } + + 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); + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/osx/OsXNativePty.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/osx/OsXNativePty.java new file mode 100644 index 000000000..8cf4accf0 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/osx/OsXNativePty.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2002-2017, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.osx; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.EnumMap; +import java.util.EnumSet; + +import org.jline.nativ.CLibrary; +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.impl.jni.JniNativePty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; + +public class OsXNativePty extends JniNativePty { + + public static OsXNativePty current(TerminalProvider provider, SystemStream systemStream) throws IOException { + try { + switch (systemStream) { + case Output: + return new OsXNativePty( + provider, + SystemStream.Output, + -1, + null, + 0, + FileDescriptor.in, + 1, + FileDescriptor.out, + ttyname(1)); + case Error: + return new OsXNativePty( + provider, + SystemStream.Error, + -1, + null, + 0, + FileDescriptor.in, + 2, + FileDescriptor.err, + ttyname(2)); + default: + throw new IllegalArgumentException("Unsupport stream for console: " + systemStream); + } + } catch (IOException e) { + throw new IOException("Not a tty", e); + } + } + + public static OsXNativePty open(TerminalProvider provider, Attributes attr, Size size) throws IOException { + int[] master = new int[1]; + int[] slave = new int[1]; + byte[] buf = new byte[64]; + CLibrary.openpty( + master, + slave, + buf, + attr != null ? termios(attr) : null, + size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); + int len = 0; + while (buf[len] != 0) { + len++; + } + String name = new String(buf, 0, len); + return new OsXNativePty( + provider, null, master[0], newDescriptor(master[0]), slave[0], newDescriptor(slave[0]), name); + } + + public OsXNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, name); + } + + public OsXNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + int slaveOut, + FileDescriptor slaveOutFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, slaveOut, slaveOutFD, name); + } + + // CONSTANTS + + private static final int VEOF = 0; + private static final int VEOL = 1; + private static final int VEOL2 = 2; + private static final int VERASE = 3; + private static final int VWERASE = 4; + private static final int VKILL = 5; + private static final int VREPRINT = 6; + private static final int VINTR = 8; + private static final int VQUIT = 9; + private static final int VSUSP = 10; + private static final int VDSUSP = 11; + private static final int VSTART = 12; + private static final int VSTOP = 13; + private static final int VLNEXT = 14; + private static final int VDISCARD = 15; + private static final int VMIN = 16; + private static final int VTIME = 17; + private static final int VSTATUS = 18; + + private static final int IGNBRK = 0x00000001; + private static final int BRKINT = 0x00000002; + private static final int IGNPAR = 0x00000004; + private static final int PARMRK = 0x00000008; + private static final int INPCK = 0x00000010; + private static final int ISTRIP = 0x00000020; + private static final int INLCR = 0x00000040; + private static final int IGNCR = 0x00000080; + private static final int ICRNL = 0x00000100; + private static final int IXON = 0x00000200; + private static final int IXOFF = 0x00000400; + private static final int IXANY = 0x00000800; + private static final int IMAXBEL = 0x00002000; + private static final int IUTF8 = 0x00004000; + + private static final int OPOST = 0x00000001; + private static final int ONLCR = 0x00000002; + private static final int OXTABS = 0x00000004; + private static final int ONOEOT = 0x00000008; + private static final int OCRNL = 0x00000010; + private static final int ONOCR = 0x00000020; + private static final int ONLRET = 0x00000040; + private static final int OFILL = 0x00000080; + private static final int NLDLY = 0x00000300; + private static final int TABDLY = 0x00000c04; + private static final int CRDLY = 0x00003000; + private static final int FFDLY = 0x00004000; + private static final int BSDLY = 0x00008000; + private static final int VTDLY = 0x00010000; + private static final int OFDEL = 0x00020000; + + private static final int CIGNORE = 0x00000001; + private static final int CS5 = 0x00000000; + private static final int CS6 = 0x00000100; + private static final int CS7 = 0x00000200; + private static final int CS8 = 0x00000300; + private static final int CSTOPB = 0x00000400; + private static final int CREAD = 0x00000800; + private static final int PARENB = 0x00001000; + private static final int PARODD = 0x00002000; + private static final int HUPCL = 0x00004000; + private static final int CLOCAL = 0x00008000; + private static final int CCTS_OFLOW = 0x00010000; + private static final int CRTS_IFLOW = 0x00020000; + private static final int CDTR_IFLOW = 0x00040000; + private static final int CDSR_OFLOW = 0x00080000; + private static final int CCAR_OFLOW = 0x00100000; + + private static final int ECHOKE = 0x00000001; + private static final int ECHOE = 0x00000002; + private static final int ECHOK = 0x00000004; + private static final int ECHO = 0x00000008; + private static final int ECHONL = 0x00000010; + private static final int ECHOPRT = 0x00000020; + private static final int ECHOCTL = 0x00000040; + private static final int ISIG = 0x00000080; + private static final int ICANON = 0x00000100; + private static final int ALTWERASE = 0x00000200; + private static final int IEXTEN = 0x00000400; + private static final int EXTPROC = 0x00000800; + private static final int TOSTOP = 0x00400000; + private static final int FLUSHO = 0x00800000; + private static final int NOKERNINFO = 0x02000000; + private static final int PENDIN = 0x20000000; + private static final int NOFLSH = 0x80000000; + + protected CLibrary.Termios toTermios(Attributes t) { + return termios(t); + } + + static CLibrary.Termios termios(Attributes t) { + CLibrary.Termios tio = new CLibrary.Termios(); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); + // Output flags + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); + // Control flags + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); + // Local flags + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); + // Control chars + tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); + tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); + tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); + tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); + tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); + tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); + tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); + tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); + tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); + tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); + tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); + tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); + tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); + tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); + tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); + tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); + tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); + tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); + return tio; + } + + protected Attributes toAttributes(CLibrary.Termios tio) { + Attributes attr = new Attributes(); + // Input flags + EnumSet iflag = attr.getInputFlags(); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); + // Output flags + EnumSet oflag = attr.getOutputFlags(); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); + // Control flags + EnumSet cflag = attr.getControlFlags(); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); + // Local flags + EnumSet lflag = attr.getLocalFlags(); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); + // Control chars + EnumMap cc = attr.getControlChars(); + cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); + cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); + cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); + cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); + cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); + cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); + cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); + cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); + cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); + cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); + cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); + cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); + cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); + cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); + cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); + cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); + cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); + cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); + // Return + return attr; + } + + 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); + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/solaris/SolarisNativePty.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/solaris/SolarisNativePty.java new file mode 100644 index 000000000..c34d11b48 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/solaris/SolarisNativePty.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2002-2017, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.solaris; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.EnumMap; +import java.util.EnumSet; + +import org.jline.nativ.CLibrary; +import org.jline.terminal.Attributes; +import org.jline.terminal.Size; +import org.jline.terminal.impl.jni.JniNativePty; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; + +public class SolarisNativePty extends JniNativePty { + + public static SolarisNativePty current(TerminalProvider provider, SystemStream systemStream) throws IOException { + try { + switch (systemStream) { + case Output: + return new SolarisNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 1, FileDescriptor.out, ttyname(1)); + case Error: + return new SolarisNativePty( + provider, systemStream, -1, null, 0, FileDescriptor.in, 2, FileDescriptor.err, ttyname(2)); + default: + throw new IllegalArgumentException("Unsupport stream for console: " + systemStream); + } + } catch (IOException e) { + throw new IOException("Not a tty", e); + } + } + + public static SolarisNativePty open(TerminalProvider provider, Attributes attr, Size size) throws IOException { + int[] master = new int[1]; + int[] slave = new int[1]; + byte[] buf = new byte[64]; + CLibrary.openpty( + master, + slave, + buf, + attr != null ? termios(attr) : null, + size != null ? new CLibrary.WinSize((short) size.getRows(), (short) size.getColumns()) : null); + int len = 0; + while (buf[len] != 0) { + len++; + } + String name = new String(buf, 0, len); + return new SolarisNativePty( + provider, + null, + master[0], + newDescriptor(master[0]), + slave[0], + newDescriptor(slave[0]), + 2, + FileDescriptor.err, + name); + } + + public SolarisNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, name); + } + + public SolarisNativePty( + TerminalProvider provider, + SystemStream systemStream, + int master, + FileDescriptor masterFD, + int slave, + FileDescriptor slaveFD, + int slaveOut, + FileDescriptor slaveOutFD, + String name) { + super(provider, systemStream, master, masterFD, slave, slaveFD, slaveOut, slaveOutFD, name); + } + // CONSTANTS + + private static final int VINTR = 0; + private static final int VQUIT = 1; + private static final int VERASE = 2; + private static final int VKILL = 3; + private static final int VEOF = 4; + private static final int VTIME = 5; + private static final int VMIN = 6; + private static final int VSWTC = 7; + private static final int VSTART = 8; + private static final int VSTOP = 9; + private static final int VSUSP = 10; + private static final int VEOL = 11; + private static final int VREPRINT = 12; + private static final int VDISCARD = 13; + private static final int VWERASE = 14; + private static final int VLNEXT = 15; + private static final int VEOL2 = 16; + + private static final int IGNBRK = 0x0000001; + private static final int BRKINT = 0x0000002; + private static final int IGNPAR = 0x0000004; + private static final int PARMRK = 0x0000010; + private static final int INPCK = 0x0000020; + private static final int ISTRIP = 0x0000040; + private static final int INLCR = 0x0000100; + private static final int IGNCR = 0x0000200; + private static final int ICRNL = 0x0000400; + private static final int IUCLC = 0x0001000; + private static final int IXON = 0x0002000; + private static final int IXANY = 0x0004000; + private static final int IXOFF = 0x0010000; + private static final int IMAXBEL = 0x0020000; + private static final int IUTF8 = 0x0040000; + + private static final int OPOST = 0x0000001; + private static final int OLCUC = 0x0000002; + private static final int ONLCR = 0x0000004; + private static final int OCRNL = 0x0000010; + private static final int ONOCR = 0x0000020; + private static final int ONLRET = 0x0000040; + private static final int OFILL = 0x0000100; + private static final int OFDEL = 0x0000200; + private static final int NLDLY = 0x0000400; + private static final int NL0 = 0x0000000; + private static final int NL1 = 0x0000400; + private static final int CRDLY = 0x0003000; + private static final int CR0 = 0x0000000; + private static final int CR1 = 0x0001000; + private static final int CR2 = 0x0002000; + private static final int CR3 = 0x0003000; + private static final int TABDLY = 0x0014000; + private static final int TAB0 = 0x0000000; + private static final int TAB1 = 0x0004000; + private static final int TAB2 = 0x0010000; + private static final int TAB3 = 0x0014000; + private static final int XTABS = 0x0014000; + private static final int BSDLY = 0x0020000; + private static final int BS0 = 0x0000000; + private static final int BS1 = 0x0020000; + private static final int VTDLY = 0x0040000; + private static final int VT0 = 0x0000000; + private static final int VT1 = 0x0040000; + private static final int FFDLY = 0x0100000; + private static final int FF0 = 0x0000000; + private static final int FF1 = 0x0100000; + + private static final int CBAUD = 0x0010017; + private static final int B0 = 0x0000000; + private static final int B50 = 0x0000001; + private static final int B75 = 0x0000002; + private static final int B110 = 0x0000003; + private static final int B134 = 0x0000004; + private static final int B150 = 0x0000005; + private static final int B200 = 0x0000006; + private static final int B300 = 0x0000007; + private static final int B600 = 0x0000010; + private static final int B1200 = 0x0000011; + private static final int B1800 = 0x0000012; + private static final int B2400 = 0x0000013; + private static final int B4800 = 0x0000014; + private static final int B9600 = 0x0000015; + private static final int B19200 = 0x0000016; + private static final int B38400 = 0x0000017; + private static final int EXTA = 0xB19200; + private static final int EXTB = 0xB38400; + private static final int CSIZE = 0x0000060; + private static final int CS5 = 0x0000000; + private static final int CS6 = 0x0000020; + private static final int CS7 = 0x0000040; + private static final int CS8 = 0x0000060; + private static final int CSTOPB = 0x0000100; + private static final int CREAD = 0x0000200; + private static final int PARENB = 0x0000400; + private static final int PARODD = 0x0001000; + private static final int HUPCL = 0x0002000; + private static final int CLOCAL = 0x0004000; + + private static final int ISIG = 0x0000001; + private static final int ICANON = 0x0000002; + private static final int XCASE = 0x0000004; + private static final int ECHO = 0x0000010; + private static final int ECHOE = 0x0000020; + private static final int ECHOK = 0x0000040; + private static final int ECHONL = 0x0000100; + private static final int NOFLSH = 0x0000200; + private static final int TOSTOP = 0x0000400; + private static final int ECHOCTL = 0x0001000; + private static final int ECHOPRT = 0x0002000; + private static final int ECHOKE = 0x0004000; + private static final int FLUSHO = 0x0010000; + private static final int PENDIN = 0x0040000; + private static final int IEXTEN = 0x0100000; + private static final int EXTPROC = 0x0200000; + + protected CLibrary.Termios toTermios(Attributes t) { + return termios(t); + } + + static CLibrary.Termios termios(Attributes t) { + CLibrary.Termios tio = new CLibrary.Termios(); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNBRK), IGNBRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.BRKINT), BRKINT, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNPAR), IGNPAR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.PARMRK), PARMRK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INPCK), INPCK, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ISTRIP), ISTRIP, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.INLCR), INLCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IGNCR), IGNCR, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.ICRNL), ICRNL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXON), IXON, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXOFF), IXOFF, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IXANY), IXANY, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IMAXBEL), IMAXBEL, tio.c_iflag); + tio.c_iflag = setFlag(t.getInputFlag(Attributes.InputFlag.IUTF8), IUTF8, tio.c_iflag); + // Output flags + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OPOST), OPOST, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLCR), ONLCR, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OXTABS), OXTABS, tio.c_oflag); + // tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOEOT), ONOEOT, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OCRNL), OCRNL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONOCR), ONOCR, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.ONLRET), ONLRET, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFILL), OFILL, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.NLDLY), NLDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.TABDLY), TABDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.CRDLY), CRDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.FFDLY), FFDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.BSDLY), BSDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.VTDLY), VTDLY, tio.c_oflag); + tio.c_oflag = setFlag(t.getOutputFlag(Attributes.OutputFlag.OFDEL), OFDEL, tio.c_oflag); + // Control flags + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CIGNORE), CIGNORE, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS5), CS5, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS6), CS6, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS7), CS7, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CS8), CS8, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CSTOPB), CSTOPB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CREAD), CREAD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARENB), PARENB, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.PARODD), PARODD, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.HUPCL), HUPCL, tio.c_cflag); + tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CLOCAL), CLOCAL, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCTS_OFLOW), CCTS_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CRTS_IFLOW), CRTS_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDTR_IFLOW), CDTR_IFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CDSR_OFLOW), CDSR_OFLOW, tio.c_cflag); + // tio.c_cflag = setFlag(t.getControlFlag(Attributes.ControlFlag.CCAR_OFLOW), CCAR_OFLOW, tio.c_cflag); + // Local flags + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOKE), ECHOKE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOE), ECHOE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOK), ECHOK, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHO), ECHO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHONL), ECHONL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOPRT), ECHOPRT, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ECHOCTL), ECHOCTL, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ISIG), ISIG, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ICANON), ICANON, tio.c_lflag); + // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.ALTWERASE), ALTWERASE, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.IEXTEN), IEXTEN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.EXTPROC), EXTPROC, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.TOSTOP), TOSTOP, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.FLUSHO), FLUSHO, tio.c_lflag); + // tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOKERNINFO), NOKERNINFO, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.PENDIN), PENDIN, tio.c_lflag); + tio.c_lflag = setFlag(t.getLocalFlag(Attributes.LocalFlag.NOFLSH), NOFLSH, tio.c_lflag); + // Control chars + tio.c_cc[VEOF] = (byte) t.getControlChar(Attributes.ControlChar.VEOF); + tio.c_cc[VEOL] = (byte) t.getControlChar(Attributes.ControlChar.VEOL); + tio.c_cc[VEOL2] = (byte) t.getControlChar(Attributes.ControlChar.VEOL2); + tio.c_cc[VERASE] = (byte) t.getControlChar(Attributes.ControlChar.VERASE); + tio.c_cc[VWERASE] = (byte) t.getControlChar(Attributes.ControlChar.VWERASE); + tio.c_cc[VKILL] = (byte) t.getControlChar(Attributes.ControlChar.VKILL); + tio.c_cc[VREPRINT] = (byte) t.getControlChar(Attributes.ControlChar.VREPRINT); + tio.c_cc[VINTR] = (byte) t.getControlChar(Attributes.ControlChar.VINTR); + tio.c_cc[VQUIT] = (byte) t.getControlChar(Attributes.ControlChar.VQUIT); + tio.c_cc[VSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VSUSP); + // tio.c_cc[VDSUSP] = (byte) t.getControlChar(Attributes.ControlChar.VDSUSP); + tio.c_cc[VSTART] = (byte) t.getControlChar(Attributes.ControlChar.VSTART); + tio.c_cc[VSTOP] = (byte) t.getControlChar(Attributes.ControlChar.VSTOP); + tio.c_cc[VLNEXT] = (byte) t.getControlChar(Attributes.ControlChar.VLNEXT); + tio.c_cc[VDISCARD] = (byte) t.getControlChar(Attributes.ControlChar.VDISCARD); + tio.c_cc[VMIN] = (byte) t.getControlChar(Attributes.ControlChar.VMIN); + tio.c_cc[VTIME] = (byte) t.getControlChar(Attributes.ControlChar.VTIME); + // tio.c_cc[VSTATUS] = (byte) t.getControlChar(Attributes.ControlChar.VSTATUS); + return tio; + } + + protected Attributes toAttributes(CLibrary.Termios tio) { + Attributes attr = new Attributes(); + // Input flags + EnumSet iflag = attr.getInputFlags(); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNBRK, IGNBRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.BRKINT, BRKINT); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNPAR, IGNPAR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.PARMRK, PARMRK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INPCK, INPCK); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ISTRIP, ISTRIP); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.INLCR, INLCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IGNCR, IGNCR); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.ICRNL, ICRNL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXON, IXON); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXOFF, IXOFF); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IXANY, IXANY); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IMAXBEL, IMAXBEL); + addFlag(tio.c_iflag, iflag, Attributes.InputFlag.IUTF8, IUTF8); + // Output flags + EnumSet oflag = attr.getOutputFlags(); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OPOST, OPOST); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLCR, ONLCR); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OXTABS, OXTABS); + // addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOEOT, ONOEOT); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OCRNL, OCRNL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONOCR, ONOCR); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.ONLRET, ONLRET); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFILL, OFILL); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.NLDLY, NLDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.TABDLY, TABDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.CRDLY, CRDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.FFDLY, FFDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.BSDLY, BSDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.VTDLY, VTDLY); + addFlag(tio.c_oflag, oflag, Attributes.OutputFlag.OFDEL, OFDEL); + // Control flags + EnumSet cflag = attr.getControlFlags(); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CIGNORE, CIGNORE); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS5, CS5); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS6, CS6); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS7, CS7); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CS8, CS8); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CSTOPB, CSTOPB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CREAD, CREAD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARENB, PARENB); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.PARODD, PARODD); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.HUPCL, HUPCL); + addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CLOCAL, CLOCAL); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCTS_OFLOW, CCTS_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CRTS_IFLOW, CRTS_IFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CDSR_OFLOW, CDSR_OFLOW); + // addFlag(tio.c_cflag, cflag, Attributes.ControlFlag.CCAR_OFLOW, CCAR_OFLOW); + // Local flags + EnumSet lflag = attr.getLocalFlags(); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOKE, ECHOKE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOE, ECHOE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOK, ECHOK); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHO, ECHO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHONL, ECHONL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOPRT, ECHOPRT); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ECHOCTL, ECHOCTL); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ISIG, ISIG); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ICANON, ICANON); + // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.ALTWERASE, ALTWERASE); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.IEXTEN, IEXTEN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.EXTPROC, EXTPROC); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.TOSTOP, TOSTOP); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.FLUSHO, FLUSHO); + // addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOKERNINFO, NOKERNINFO); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.PENDIN, PENDIN); + addFlag(tio.c_lflag, lflag, Attributes.LocalFlag.NOFLSH, NOFLSH); + // Control chars + EnumMap cc = attr.getControlChars(); + cc.put(Attributes.ControlChar.VEOF, (int) tio.c_cc[VEOF]); + cc.put(Attributes.ControlChar.VEOL, (int) tio.c_cc[VEOL]); + cc.put(Attributes.ControlChar.VEOL2, (int) tio.c_cc[VEOL2]); + cc.put(Attributes.ControlChar.VERASE, (int) tio.c_cc[VERASE]); + cc.put(Attributes.ControlChar.VWERASE, (int) tio.c_cc[VWERASE]); + cc.put(Attributes.ControlChar.VKILL, (int) tio.c_cc[VKILL]); + cc.put(Attributes.ControlChar.VREPRINT, (int) tio.c_cc[VREPRINT]); + cc.put(Attributes.ControlChar.VINTR, (int) tio.c_cc[VINTR]); + cc.put(Attributes.ControlChar.VQUIT, (int) tio.c_cc[VQUIT]); + cc.put(Attributes.ControlChar.VSUSP, (int) tio.c_cc[VSUSP]); + // cc.put(Attributes.ControlChar.VDSUSP, (int) tio.c_cc[VDSUSP]); + cc.put(Attributes.ControlChar.VSTART, (int) tio.c_cc[VSTART]); + cc.put(Attributes.ControlChar.VSTOP, (int) tio.c_cc[VSTOP]); + cc.put(Attributes.ControlChar.VLNEXT, (int) tio.c_cc[VLNEXT]); + cc.put(Attributes.ControlChar.VDISCARD, (int) tio.c_cc[VDISCARD]); + cc.put(Attributes.ControlChar.VMIN, (int) tio.c_cc[VMIN]); + cc.put(Attributes.ControlChar.VTIME, (int) tio.c_cc[VTIME]); + // cc.put(Attributes.ControlChar.VSTATUS, (int) tio.c_cc[VSTATUS]); + // Return + return attr; + } + + 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); + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinConsoleWriter.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinConsoleWriter.java new file mode 100644 index 000000000..fa4a9f6e2 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinConsoleWriter.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002-2017, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.win; + +import java.io.IOException; + +import org.jline.nativ.Kernel32; +import org.jline.terminal.impl.AbstractWindowsConsoleWriter; + +class NativeWinConsoleWriter extends AbstractWindowsConsoleWriter { + + private final long console; + private final int[] writtenChars = new int[1]; + + public NativeWinConsoleWriter(long console) { + this.console = console; + } + + @Override + protected void writeConsole(char[] text, int len) throws IOException { + if (Kernel32.WriteConsoleW(console, text, len, writtenChars, 0) == 0) { + throw new IOException("Failed to write to console: " + Kernel32.getLastErrorMessage()); + } + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinSysTerminal.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinSysTerminal.java new file mode 100644 index 000000000..6e8abfad6 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/NativeWinSysTerminal.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2002-2020, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.win; + +import java.io.BufferedWriter; +import java.io.IOError; +import java.io.IOException; +import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.function.IntConsumer; + +import org.jline.nativ.Kernel32; +import org.jline.nativ.Kernel32.CONSOLE_SCREEN_BUFFER_INFO; +import org.jline.nativ.Kernel32.INPUT_RECORD; +import org.jline.nativ.Kernel32.KEY_EVENT_RECORD; +import org.jline.terminal.Cursor; +import org.jline.terminal.Size; +import org.jline.terminal.impl.AbstractWindowsTerminal; +import org.jline.terminal.spi.SystemStream; +import org.jline.terminal.spi.TerminalProvider; +import org.jline.utils.InfoCmp; +import org.jline.utils.OSUtils; + +import static org.jline.nativ.Kernel32.FORMAT_MESSAGE_FROM_SYSTEM; +import static org.jline.nativ.Kernel32.FormatMessageW; +import static org.jline.nativ.Kernel32.GetConsoleScreenBufferInfo; +import static org.jline.nativ.Kernel32.GetLastError; +import static org.jline.nativ.Kernel32.GetStdHandle; +import static org.jline.nativ.Kernel32.INVALID_HANDLE_VALUE; +import static org.jline.nativ.Kernel32.STD_ERROR_HANDLE; +import static org.jline.nativ.Kernel32.STD_INPUT_HANDLE; +import static org.jline.nativ.Kernel32.STD_OUTPUT_HANDLE; +import static org.jline.nativ.Kernel32.WaitForSingleObject; +import static org.jline.nativ.Kernel32.readConsoleInputHelper; + +public class NativeWinSysTerminal extends AbstractWindowsTerminal { + + private static final long consoleIn = GetStdHandle(STD_INPUT_HANDLE); + private static final long consoleOut = GetStdHandle(STD_OUTPUT_HANDLE); + private static final long consoleErr = GetStdHandle(STD_ERROR_HANDLE); + + public static NativeWinSysTerminal createTerminal( + TerminalProvider provider, + SystemStream systemStream, + String name, + String type, + boolean ansiPassThrough, + Charset encoding, + boolean nativeSignals, + SignalHandler signalHandler, + boolean paused) + throws IOException { + // Get input console mode + int[] inMode = new int[1]; + if (Kernel32.GetConsoleMode(consoleIn, inMode) == 0) { + throw new IOException("Failed to get console mode: " + getLastErrorMessage()); + } + // Get output console and mode + long console = getConsole(systemStream); + int[] outMode = new int[1]; + if (Kernel32.GetConsoleMode(console, outMode) == 0) { + throw new IOException("Failed to get console mode: " + getLastErrorMessage()); + } + // Create writer + Writer writer; + if (ansiPassThrough) { + type = type != null ? type : OSUtils.IS_CONEMU ? TYPE_WINDOWS_CONEMU : TYPE_WINDOWS; + writer = newConsoleWriter(console); + } else { + if (enableVtp(console, outMode[0])) { + type = type != null ? type : TYPE_WINDOWS_VTP; + writer = newConsoleWriter(console); + } else if (OSUtils.IS_CONEMU) { + type = type != null ? type : TYPE_WINDOWS_CONEMU; + writer = newConsoleWriter(console); + } else { + type = type != null ? type : TYPE_WINDOWS; + writer = new WindowsAnsiWriter(new BufferedWriter(newConsoleWriter(console))); + } + } + // Create terminal + NativeWinSysTerminal terminal = new NativeWinSysTerminal( + provider, + systemStream, + writer, + name, + type, + encoding, + nativeSignals, + signalHandler, + consoleIn, + inMode[0], + console, + outMode[0]); + // Start input pump thread + if (!paused) { + terminal.resume(); + } + return terminal; + } + + public static long getConsole(SystemStream systemStream) { + long console; + switch (systemStream) { + case Output: + console = consoleOut; + break; + case Error: + console = consoleErr; + break; + default: + throw new IllegalArgumentException("Unsupported stream for console: " + systemStream); + } + return console; + } + + private static boolean enableVtp(long console, int outMode) { + return Kernel32.SetConsoleMode(console, outMode | AbstractWindowsTerminal.ENABLE_VIRTUAL_TERMINAL_PROCESSING) + != 0; + } + + private static Writer newConsoleWriter(long console) { + return new NativeWinConsoleWriter(console); + } + + public static boolean isWindowsSystemStream(SystemStream 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; + } + + NativeWinSysTerminal( + TerminalProvider provider, + SystemStream systemStream, + Writer writer, + String name, + String type, + Charset encoding, + boolean nativeSignals, + SignalHandler signalHandler, + long inConsole, + int inMode, + long outConsole, + int outMode) + throws IOException { + super( + provider, + systemStream, + writer, + name, + type, + encoding, + nativeSignals, + signalHandler, + inConsole, + inMode, + outConsole, + outMode); + } + + @Override + protected int getConsoleMode(Long console) { + int[] mode = new int[1]; + if (Kernel32.GetConsoleMode(console, mode) == 0) { + return -1; + } + return mode[0]; + } + + @Override + protected void setConsoleMode(Long console, int mode) { + Kernel32.SetConsoleMode(console, mode); + } + + public Size getSize() { + CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(); + Kernel32.GetConsoleScreenBufferInfo(outConsole, info); + return new Size(info.windowWidth(), info.windowHeight()); + } + + @Override + public Size getBufferSize() { + CONSOLE_SCREEN_BUFFER_INFO info = new CONSOLE_SCREEN_BUFFER_INFO(); + Kernel32.GetConsoleScreenBufferInfo(outConsole, info); + return new Size(info.size.x, info.size.y); + } + + protected boolean processConsoleInput() throws IOException { + INPUT_RECORD[] events; + if (inConsole != INVALID_HANDLE_VALUE && WaitForSingleObject(inConsole, 100) == 0) { + events = readConsoleInputHelper(inConsole, 1, false); + } else { + return false; + } + + boolean flush = false; + for (INPUT_RECORD event : events) { + if (event.eventType == INPUT_RECORD.KEY_EVENT) { + KEY_EVENT_RECORD keyEvent = event.keyEvent; + processKeyEvent(keyEvent.keyDown, keyEvent.keyCode, keyEvent.uchar, keyEvent.controlKeyState); + flush = true; + } else if (event.eventType == INPUT_RECORD.WINDOW_BUFFER_SIZE_EVENT) { + raise(Signal.WINCH); + } else if (event.eventType == INPUT_RECORD.MOUSE_EVENT) { + processMouseEvent(event.mouseEvent); + flush = true; + } else if (event.eventType == 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(outConsole, 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(); + } +} diff --git a/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/WindowsAnsiWriter.java b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/WindowsAnsiWriter.java new file mode 100644 index 000000000..32d2a32e3 --- /dev/null +++ b/terminal-jni/src/main/java/org/jline/terminal/impl/jni/win/WindowsAnsiWriter.java @@ -0,0 +1,386 @@ +/* + * Copyright (c) 2009-2018, the original author(s). + * + * This software is distributable under the BSD license. See the terms of the + * BSD license in the documentation provided with this software. + * + * https://opensource.org/licenses/BSD-3-Clause + */ +package org.jline.terminal.impl.jni.win; + +import java.io.IOException; +import java.io.Writer; + +import org.jline.nativ.Kernel32; +import org.jline.utils.AnsiWriter; +import org.jline.utils.Colors; + +import static org.jline.nativ.Kernel32.*; + +/** + * 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 long 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: " + Kernel32.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(Kernel32.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.copy()) == 0) { + throw new IOException(Kernel32.getLastErrorMessage()); + } + } + + @Override + protected void processEraseScreen(int eraseOption) throws IOException { + getConsoleInfo(); + int[] written = new int[1]; + switch (eraseOption) { + case ERASE_SCREEN: + COORD topLeft = new COORD(); + topLeft.x = 0; + topLeft.y = 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(); + topLeft2.x = 0; + topLeft2.y = 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.copy(), written); + FillConsoleOutputCharacterW(console, ' ', lengthToEnd, info.cursorPosition.copy(), written); + break; + default: + break; + } + } + + @Override + protected void processEraseLine(int eraseOption) throws IOException { + getConsoleInfo(); + int[] written = new int[1]; + switch (eraseOption) { + case ERASE_LINE: + COORD leftColCurrRow = info.cursorPosition.copy(); + leftColCurrRow.x = 0; + FillConsoleOutputAttribute(console, originalColors, info.size.x, leftColCurrRow, written); + FillConsoleOutputCharacterW(console, ' ', info.size.x, leftColCurrRow, written); + break; + case ERASE_LINE_TO_BEGINING: + COORD leftColCurrRow2 = info.cursorPosition.copy(); + leftColCurrRow2.x = 0; + 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.copy(), written); + FillConsoleOutputCharacterW(console, ' ', lengthToLastCol, info.cursorPosition.copy(), written); + break; + default: + break; + } + } + + protected void processCursorUpLine(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x = 0; + info.cursorPosition.y -= (short) count; + applyCursorPosition(); + } + + protected void processCursorDownLine(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x = 0; + info.cursorPosition.y += (short) count; + applyCursorPosition(); + } + + @Override + protected void processCursorLeft(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x -= (short) count; + applyCursorPosition(); + } + + @Override + protected void processCursorRight(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.x += (short) 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) count; + applyCursorPosition(); + } + if (nb > 0) { + SMALL_RECT scroll = info.window.copy(); + scroll.top = 0; + COORD org = new COORD(); + org.x = 0; + org.y = (short) (-nb); + CHAR_INFO info = new CHAR_INFO(); + info.unicodeChar = ' '; + info.attributes = originalColors; + ScrollConsoleScreenBuffer(console, scroll, scroll, org, info); + } + } + + @Override + protected void processCursorUp(int count) throws IOException { + getConsoleInfo(); + info.cursorPosition.y -= (short) 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(); + org.x = 0; + org.y = (short) (info.cursorPosition.y + optionInt); + CHAR_INFO info = new CHAR_INFO(); + info.attributes = originalColors; + info.unicodeChar = ' '; + if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) { + throw new IOException(Kernel32.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(); + org.x = 0; + org.y = (short) (info.cursorPosition.y - optionInt); + CHAR_INFO info = new CHAR_INFO(); + info.attributes = originalColors; + info.unicodeChar = ' '; + if (ScrollConsoleScreenBuffer(console, scroll, scroll, org, info) == 0) { + throw new IOException(Kernel32.getLastErrorMessage()); + } + } + + @Override + protected void processChangeWindowTitle(String title) { + SetConsoleTitle(title); + } +} diff --git a/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/native-image.properties b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/native-image.properties new file mode 100644 index 000000000..1a4e8e5a5 --- /dev/null +++ b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/native-image.properties @@ -0,0 +1 @@ +Args = -H:ResourceConfigurationResources=${.}/resource-config.json -H:ReflectionConfigurationResources=${.}/reflection-config.json diff --git a/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/reflection-config.json b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/reflection-config.json new file mode 100644 index 000000000..d769687cc --- /dev/null +++ b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/reflection-config.json @@ -0,0 +1,11 @@ +[ + { + "name" : "org.jline.terminal.impl.jni.JniTerminalProvider", + "allDeclaredConstructors" : true, + "allPublicConstructors" : true, + "allDeclaredMethods" : false, + "allPublicMethods" : false, + "allDeclaredFields" : false, + "allPublicFields" : false + } +] \ No newline at end of file diff --git a/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/resource-config.json b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/resource-config.json new file mode 100644 index 000000000..f187ea298 --- /dev/null +++ b/terminal-jni/src/main/resources/META-INF/native-image/org.jline/jline-terminal-jni/resource-config.json @@ -0,0 +1,5 @@ +{ + "resources": [ + {"pattern": "META-INF/services/org/jline/terminal/provider/.*"} + ] +} \ No newline at end of file diff --git a/terminal-jni/src/main/resources/META-INF/services/org/jline/terminal/provider/jni b/terminal-jni/src/main/resources/META-INF/services/org/jline/terminal/provider/jni new file mode 100644 index 000000000..10de0e864 --- /dev/null +++ b/terminal-jni/src/main/resources/META-INF/services/org/jline/terminal/provider/jni @@ -0,0 +1,16 @@ +# +# 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. +# +class = org.jline.terminal.impl.jni.JniTerminalProvider diff --git a/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java b/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java index 32993ba98..fd952afc6 100644 --- a/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java +++ b/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java @@ -51,9 +51,10 @@ public final class TerminalBuilder { public static final String PROP_CODEPAGE = "org.jline.terminal.codepage"; public static final String PROP_TYPE = "org.jline.terminal.type"; public static final String PROP_PROVIDERS = "org.jline.terminal.providers"; - public static final String PROP_PROVIDERS_DEFAULT = "ffm,jansi,jna,exec"; + public static final String PROP_PROVIDERS_DEFAULT = "ffm,jni,jansi,jna,exec"; public static final String PROP_JNA = "org.jline.terminal.jna"; public static final String PROP_JANSI = "org.jline.terminal.jansi"; + public static final String PROP_JNI = "org.jline.terminal.jni"; public static final String PROP_EXEC = "org.jline.terminal.exec"; public static final String PROP_FFM = "org.jline.terminal.ffm"; public static final String PROP_DUMB = "org.jline.terminal.dumb"; @@ -140,6 +141,7 @@ public static TerminalBuilder builder() { private String providers; private Boolean jna; private Boolean jansi; + private Boolean jni; private Boolean exec; private Boolean ffm; private Boolean dumb; @@ -193,7 +195,12 @@ public TerminalBuilder jna(boolean jna) { } public TerminalBuilder jansi(boolean jansi) { - this.jansi = jansi; + this.jansi = this.jansi; + return this; + } + + public TerminalBuilder jni(boolean jni) { + this.jni = this.jni; return this; } @@ -387,6 +394,10 @@ private Terminal doBuild() throws IOException { if (jansi == null) { jansi = getBoolean(PROP_JANSI, true); } + Boolean nativ = this.jni; + if (nativ == null) { + nativ = getBoolean(PROP_JNI, true); + } Boolean exec = this.exec; if (exec == null) { exec = getBoolean(PROP_EXEC, true); @@ -431,6 +442,16 @@ private Terminal doBuild() throws IOException { exception.addSuppressed(t); } } + if (nativ) { + try { + TerminalProvider provider = TerminalProvider.load("nativ"); + provider.isSystemStream(SystemStream.Output); + providers.add(provider); + } catch (Throwable t) { + Log.debug("Unable to load Native support: ", t); + exception.addSuppressed(t); + } + } if (exec) { try { TerminalProvider provider = TerminalProvider.load("exec"); @@ -532,7 +553,7 @@ private Terminal doBuild() throws IOException { } } } - if (terminal == null && OSUtils.IS_WINDOWS && !jna && !jansi && (dumb == null || !dumb)) { + if (terminal == null && OSUtils.IS_WINDOWS && !jna && !jansi && !nativ && (dumb == null || !dumb)) { throw new IllegalStateException("Unable to create a system terminal. On windows, either " + "JNA or JANSI library is required. Make sure to add one of those in the classpath."); } diff --git a/terminal/src/main/java/org/jline/terminal/impl/Diag.java b/terminal/src/main/java/org/jline/terminal/impl/Diag.java index b35e62555..ecef6c13f 100644 --- a/terminal/src/main/java/org/jline/terminal/impl/Diag.java +++ b/terminal/src/main/java/org/jline/terminal/impl/Diag.java @@ -82,6 +82,16 @@ static void diag(PrintStream out) { } out.println(); + out.println("JniSupport"); + out.println("================="); + try { + TerminalProvider provider = TerminalProvider.load("jni"); + testProvider(out, provider); + } catch (Throwable t) { + out.println("JNI support not available: " + t); + } + out.println(); + // Exec out.println("Exec Support"); out.println("=================");