|
| 1 | +--- |
| 2 | +date: '2026-04-18T12:00:00+02:00' |
| 3 | +draft: false |
| 4 | +title: 'Windows Native Access' |
| 5 | +weight: 16 |
| 6 | +--- |
| 7 | + |
| 8 | +On Windows, Æsh Readline needs access to the Windows Console API (Kernel32) for raw terminal input, console mode control, and terminal size detection. Starting with version 3.6, the `terminal-tty` module ships as a **multi-release JAR** with two implementations: |
| 9 | + |
| 10 | +| Java Version | Implementation | Native Code Required | |
| 11 | +|-------------|----------------|---------------------| |
| 12 | +| 8 -- 21 | JNI (`aesh-console.dll`) | Yes | |
| 13 | +| 22+ | FFM (`java.lang.foreign`) | No | |
| 14 | + |
| 15 | +On Java 22+, the Foreign Function & Memory API calls Kernel32 directly from pure Java -- no DLL, no native compilation, no cross-compiler toolchain. |
| 16 | + |
| 17 | +## Runtime Requirements |
| 18 | + |
| 19 | +### Java 22+ |
| 20 | + |
| 21 | +Applications running on Java 22+ must enable native access for the FFM API: |
| 22 | + |
| 23 | +``` |
| 24 | +java --enable-native-access=ALL-UNNAMED -jar myapp.jar |
| 25 | +``` |
| 26 | + |
| 27 | +Without this flag, Java 22-23 prints a warning and Java 24+ throws an `IllegalCallerException`. |
| 28 | + |
| 29 | +### Java 23+ |
| 30 | + |
| 31 | +Starting with Java 23, the JVM also warns when JNI loads native libraries without `--enable-native-access`. This means the flag is required on Java 23+ regardless of which implementation is active. The FFM path is the better choice here since it eliminates the DLL entirely. |
| 32 | + |
| 33 | +### Java 8 -- 22 |
| 34 | + |
| 35 | +No special flags are needed. The JNI implementation loads `aesh-console.dll` from the JAR automatically. |
| 36 | + |
| 37 | +## How It Works |
| 38 | + |
| 39 | +The multi-release JAR contains two versions of `WinConsoleNative`: |
| 40 | + |
| 41 | +``` |
| 42 | +terminal-tty.jar |
| 43 | +├── org/aesh/terminal/tty/impl/WinConsoleNative.class (JNI, Java 8) |
| 44 | +├── META-INF/versions/22/org/aesh/terminal/tty/impl/WinConsoleNative.class (FFM, Java 22+) |
| 45 | +├── META-INF/MANIFEST.MF (Multi-Release: true) |
| 46 | +└── native/windows-x86_64/aesh-console.dll (for JNI fallback) |
| 47 | +``` |
| 48 | + |
| 49 | +The JVM automatically selects the correct class based on the runtime version. No configuration or code changes are needed -- callers like `WinSysTerminal` and `AbstractWindowsTerminal` use the same API regardless of which implementation is active. |
| 50 | + |
| 51 | +Both implementations wrap these Windows Console API functions: |
| 52 | + |
| 53 | +| Function | Purpose | |
| 54 | +|----------|---------| |
| 55 | +| `GetStdHandle` | Obtain stdin/stdout/stderr handles | |
| 56 | +| `GetConsoleMode` / `SetConsoleMode` | Control raw mode, echo, VT processing | |
| 57 | +| `GetConsoleOutputCP` | Detect console encoding | |
| 58 | +| `GetConsoleScreenBufferInfo` | Query terminal width and height | |
| 59 | +| `ReadConsoleInputW` | Read key events and window resize events | |
| 60 | +| `WriteConsoleW` | Write Unicode output to the console | |
| 61 | + |
| 62 | +## Building from Source |
| 63 | + |
| 64 | +The multi-release JAR is built automatically based on the JDK used: |
| 65 | + |
| 66 | +**With Java 22+** -- produces a multi-release JAR with both JNI and FFM variants: |
| 67 | + |
| 68 | +```bash |
| 69 | +export JAVA_HOME=/path/to/jdk-24 |
| 70 | +mvn clean package |
| 71 | +``` |
| 72 | + |
| 73 | +**With Java 8-21** -- produces a standard JAR with only the JNI variant: |
| 74 | + |
| 75 | +```bash |
| 76 | +export JAVA_HOME=/path/to/jdk-21 |
| 77 | +mvn clean package |
| 78 | +``` |
| 79 | + |
| 80 | +For releases, build with Java 22+ to include the FFM implementation. |
| 81 | + |
| 82 | +## GraalVM Native Image |
| 83 | + |
| 84 | +The FFM implementation is compatible with GraalVM native-image (25+). For GraalVM 23-24, use the JNI implementation -- the `resource-config.json` in the JAR ensures the DLL is included in native images. |
| 85 | + |
| 86 | +## Maven Configuration for Downstream Projects |
| 87 | + |
| 88 | +If your project uses `maven-surefire-plugin` or `maven-exec-plugin` and runs on Java 22+, add the native access flag: |
| 89 | + |
| 90 | +```xml |
| 91 | +<plugin> |
| 92 | + <groupId>org.apache.maven.plugins</groupId> |
| 93 | + <artifactId>maven-surefire-plugin</artifactId> |
| 94 | + <configuration> |
| 95 | + <argLine>--enable-native-access=ALL-UNNAMED</argLine> |
| 96 | + </configuration> |
| 97 | +</plugin> |
| 98 | +``` |
| 99 | + |
| 100 | +To avoid breaking builds on older JDKs, use a profile: |
| 101 | + |
| 102 | +```xml |
| 103 | +<profile> |
| 104 | + <id>java22-native-access</id> |
| 105 | + <activation> |
| 106 | + <jdk>[22,)</jdk> |
| 107 | + </activation> |
| 108 | + <build> |
| 109 | + <plugins> |
| 110 | + <plugin> |
| 111 | + <groupId>org.apache.maven.plugins</groupId> |
| 112 | + <artifactId>maven-surefire-plugin</artifactId> |
| 113 | + <configuration> |
| 114 | + <argLine>--enable-native-access=ALL-UNNAMED</argLine> |
| 115 | + </configuration> |
| 116 | + </plugin> |
| 117 | + </plugins> |
| 118 | + </build> |
| 119 | +</profile> |
| 120 | +``` |
| 121 | + |
| 122 | +## Cygwin and MSYS2 |
| 123 | + |
| 124 | +When running under Cygwin or MSYS2, Æsh Readline detects the POSIX-compatible environment and uses PTY-based terminal access instead of the Windows Console API. Neither JNI nor FFM is used in this case. |
0 commit comments