# Building imgui-java WCHAR32 Fork This fork adds `IMGUI_USE_WCHAR32=1` support so that ImGui can handle Unicode codepoints above U+FFFF (emoji, CJK extensions, etc.). Published as `org.nnstd.imgui:imgui-java-*:1.90.0-wchar32+mrlokopN`. ## Repository - **Maven**: `https://mvn.mrlokop.dev/releases` - **Git**: `https://git.mrlokop.dev/shlepa/imgui-java` ## Coordinates ```xml mrlokop https://mvn.mrlokop.dev/releases org.nnstd.imgui imgui-java-binding 1.90.0-wchar32+mrlokop2 org.nnstd.imgui imgui-java-lwjgl3 1.90.0-wchar32+mrlokop2 org.nnstd.imgui imgui-java-natives-macos 1.90.0-wchar32+mrlokop2 org.nnstd.imgui imgui-java-natives-linux 1.90.0-wchar32+mrlokop2 org.nnstd.imgui imgui-java-natives-windows 1.90.0-wchar32+mrlokop2 org.nnstd.imgui imgui-java-app 1.90.0-wchar32+mrlokop2 ``` For Gradle (Kotlin DSL): ```kotlin repositories { maven("https://mvn.mrlokop.dev/releases") } dependencies { implementation("org.nnstd.imgui:imgui-java-binding:1.90.0-wchar32+mrlokop2") implementation("org.nnstd.imgui:imgui-java-lwjgl3:1.90.0-wchar32+mrlokop2") runtimeOnly("org.nnstd.imgui:imgui-java-natives-linux:1.90.0-wchar32+mrlokop2") } ``` ## Version History | Version | Linux toolchain | Notes | |---------|-----------------|-------| | `+mrlokop1` | musl (`x86_64-unknown-linux-musl`) | No glibc dependency; static stdlib | | `+mrlokop2` | glibc (`x86_64-unknown-linux-gnu`) | Standard Linux distros; links libc.so.6 | ## Prerequisites (macOS build host) All cross-compilation is done from a macOS machine. ```bash # Java 17 (build toolchain) sdk install java 17.0.18.1-sem # via SDKMAN # Native build tools brew install ant brew install mingw-w64 # Windows cross-compiler # Linux glibc cross-compiler (for +mrlokop2 / recommended) brew tap messense/macos-cross-toolchains brew install x86_64-unknown-linux-gnu # Linux musl cross-compiler (for +mrlokop1 / no-glibc builds) brew install x86_64-unknown-linux-musl ``` ## Build Steps ### 1. Clone and initialise submodules ```bash git clone https://git.mrlokop.dev/shlepa/imgui-java cd imgui-java git submodule update --init --recursive ``` ### 2. Build vendored FreeType for each platform FreeType must be cross-compiled before the JNI natives. ```bash buildSrc/scripts/vendor_freetype.sh macos buildSrc/scripts/vendor_freetype.sh linux buildSrc/scripts/vendor_freetype.sh windows ``` Each invocation drops a static `libfreetype.a` into `build/vendor/freetype/lib/`. The macOS build uses `lipo` to create a universal (arm64 + x86_64) library automatically. ### 3. Build JNI natives ```bash export JAVA_HOME=/path/to/java17 # macOS universal (arm64 + x86_64) JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh macos # Linux x86_64 (glibc) JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh linux # Windows x86_64 (mingw) JAVA_HOME=$JAVA_HOME buildSrc/scripts/build.sh windows ``` Compiled natives land in: - `bin/macos64/libimgui-java64.dylib` - `bin/macosarm64/libimgui-java64.dylib` - `bin/linux64/libimgui-java64.so` - `bin/windows64/imgui-java64.dll` ### 4. Bump the version (when releasing a new patch) Edit `build.gradle` (root), increment the `+mrlokopN` suffix: ```groovy version = '1.90.0-wchar32+mrlokop3' // was +mrlokop2 ``` ### 5. Publish to Maven Publish each platform's natives separately because the artifact classifier differs. Run all four publish commands: ```bash JH=/path/to/java17 GW="JAVA_HOME=$JH ./gradlew" # Binding + LWJGL3 (platform-independent JARs) $GW imgui-binding:publishImguiPublicationToMrLokopRepository \ imgui-lwjgl3:publishImguiPublicationToMrLokopRepository # Native artifacts (one per platform) $GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=macos $GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=linux $GW imgui-binding-natives:publishImguiPublicationToMrLokopRepository -PdeployType=windows # Fat app JAR (all natives bundled) $GW imgui-app:publishImguiPublicationToMrLokopRepository ``` ## Switching Linux Toolchain (musl vs glibc) **glibc** (default, `+mrlokop2`) — standard Linux distributions: In `buildSrc/src/main/groovy/tool/generator/GenerateLibs.groovy`: ```groovy linux64.compilerPrefix = 'x86_64-unknown-linux-gnu-' ``` In `buildSrc/scripts/vendor_freetype.sh`: ```bash linux) build_freetype "-fPIC" "--host=x86_64-unknown-linux-gnu" "lib/libfreetype.a" "x86_64-unknown-linux-gnu-gcc" ;; ``` **musl** (`+mrlokop1`) — no glibc dependency, portable: In `GenerateLibs.groovy`: ```groovy linux64.compilerPrefix = 'x86_64-unknown-linux-musl-' linux64.linkerFlags = linux64.linkerFlags.replace('-Wl,-wrap,memcpy', '') // After AntScriptGenerator.generate(), neutralise memcpy_wrap.c: def memcpyWrap = new File("$jniDir/memcpy_wrap.c") if (memcpyWrap.exists()) { memcpyWrap.text = '/* disabled for musl */' } ``` In `vendor_freetype.sh`: ```bash linux) build_freetype "-fPIC" "--host=x86_64-unknown-linux-musl" "lib/libfreetype.a" "x86_64-unknown-linux-musl-gcc" ;; ``` ## Key Changes vs Upstream | File | Change | |------|--------| | `imgui-binding/src/main/native/imconfig.h` | Uncommented `#define IMGUI_USE_WCHAR32` | | `ImFontGlyphRangesBuilder.java` | `UNICODE_CODEPOINT_MAX=0x10FFFF`; `addChar(int)`; 32-bit LE encoding in `addRanges`/`buildRanges` | | `ImFontConfig.java` | Added `setGlyphRanges(int[])` overload | | `ImFontAtlas.java` | `RETURN_GLYPH_2_SHORT` macro uses `sizeof(ImWchar)/sizeof(jshort)` multiplier | | `build.gradle` | Version hardcoded to `1.90.0-wchar32+mrlokopN` | | `publish.gradle` | `groupId=org.nnstd.imgui`, repo=`https://mvn.mrlokop.dev/releases` | | `GenerateLibs.groovy` | Linux uses `x86_64-unknown-linux-gnu-` prefix | | `vendor_freetype.sh` | `--host=` flags for all platforms; macOS universal via `lipo` | ## 32-bit Glyph Range Encoding With `IMGUI_USE_WCHAR32`, `ImWchar` is `uint32_t` (4 bytes). The Java binding stores glyph ranges as `short[]` where each 32-bit codepoint is packed as two little-endian `jshort`s: ``` ranges = [lo_lo, lo_hi, hi_lo, hi_hi, ..., 0, 0] ^--- pair 1 ---^ ^--- pair 2+ ---^ terminator ``` Use `ImFontGlyphRangesBuilder.buildRanges()` or `ImFontConfig.setGlyphRanges(int[])` to handle this automatically: ```java // int[] variant (simplest) fontConfig.setGlyphRanges(new int[]{ 0x1F000, 0x1F9FF, 0 }); // Builder variant ImFontGlyphRangesBuilder builder = new ImFontGlyphRangesBuilder(); builder.addText("Hello 🌍"); short[] ranges = builder.buildRanges(); fontConfig.setGlyphRanges(ranges); ```