Skip to content

Architecture: Unify curses-java and curses-themes with shared implementation #233

@sfloess

Description

@sfloess

Problem

We currently maintain two parallel implementations of the same theme system:

  1. curses-java (Java) - 192 source files, Java 21 FFM calling ncurses
  2. curses-themes (Python) - Pure Python calling ncurses via stdlib

Both have identical Theme3D interfaces and duplicate rendering logic (~500-600 lines):

  • 11+ themes (Borland, Borland3D, DBase3, DBase4, DBase4_3D, DOS, TRS-80, TI-99/4A, etc.)
  • Color management, 3D border rendering, shadow/highlight/lowlight logic
  • draw_box_3d() implementation (~125 lines each)

Pain point: Every new theme or rendering feature must be manually duplicated and kept in sync.


Multi-AI Analysis Results

Ran ai-prompt workflow with 3 AI models (Opus, Sonnet, Haiku) to evaluate solutions.

Consensus Level: HIGH (92% confidence)
Models Agreed: 3/3


🏆 Recommended Solutions (Ranked)

#1: Rust + C ABI with UniFFI ⭐⭐⭐⭐⭐

All 3 models ranked this #1

Approach:

  • Write theme rendering logic once in Rust
  • Compile to shared library (.so/.dylib/.dll)
  • Java calls via FFM (same pattern as existing `NcursesBridge.java`)
  • Python calls via ctypes (built-in)
  • Mozilla's UniFFI auto-generates bindings from .udl interface

Why it wins:

  • ✅ TRUE single implementation
  • ✅ Already use FFM in Java (familiar pattern)
  • ✅ Memory-safe (unlike C)
  • ✅ Rust can call ncurses directly (ncurses-rs crate)
  • ✅ Production-proven (Mozilla Firefox uses UniFFI)
  • ✅ No C code required

Reference: https://github.com/shmuelamar/rs-py-java

Project structure:
```
curses-themes-core/ (Rust crate)
src/lib.rs -- exports extern "C" functions
src/theme.rs -- Theme/Theme3D traits
src/borland3d.rs -- Borland3DTheme
src/render.rs -- draw_box_3d, shadows
Cargo.toml -- depends on ncurses-rs

theme.udl -- UniFFI interface definition
```

Cons:

  • ⚠️ Requires learning Rust
  • ⚠️ Build complexity (Cargo + Maven + setuptools)

#2: Shared JSON Theme Data + Thin Rendering ⭐⭐⭐⭐

Pragmatic fallback - all 3 models agree viable

Approach:

  • Extract all theme DATA (colors, border chars, shadow offsets) to JSON/YAML
  • Keep thin rendering engine (~125 lines) in each language
  • Both read same JSON files

Example:
```json
{
"name": "Borland 3D",
"border_chars": "+-+||+-+",
"colors": {
"shadow": {"fg": "BLACK", "bg": "BLACK"},
"highlight": {"fg": "WHITE", "bg": "CYAN"}
},
"shadow_offset": {"x": 2, "y": 1}
}
```

Why viable:

  • The DATA is what's mostly duplicated (theme definitions)
  • The rendering ALGORITHM is small (~125 lines) and stable

Pros:

  • ✅ Zero new languages
  • ✅ Simple, low risk
  • ✅ Human-readable themes
  • ✅ Users can create custom themes

Cons:

  • ⚠️ Rendering logic still duplicated (~125 lines per language)

#3: GraalVM Native Image ⭐⭐⭐

If keeping Java as source of truth

  • Compile Java to .so with `@CEntryPoint`
  • Python calls via ctypes

Cons:

  • ⚠️ 50-100MB binary (embeds JVM substrate)
  • ⚠️ Complex toolchain

#4: Zig with C ABI ⭐⭐

Simpler than Rust, less mature ecosystem


❌ Eliminated Options

  • C library - Rejected (don't want to write/maintain C)
  • WebAssembly/WASI - No termios support, can't call ncurses
  • Go c-shared - Embeds GC runtime (5-10MB overhead, per-call CGo cost)
  • HTTP/IPC service - Too slow for terminal rendering (sub-ms latency needed)
  • GraalVM polyglot - Already considered
  • Jython - Limited to Python 2.7

🎯 Decision Needed

Option A: Learn Rust, build shared library (best long-term, true DRY)
Option B: Use JSON data files (easiest short-term, accept ~125 lines duplication)


Next Steps

  1. Decide approach: Rust vs JSON vs defer
  2. If Rust:
    • Create `curses-themes-core` Rust crate
    • Port Borland3DTheme as proof-of-concept
    • Set up UniFFI bindings
    • Update Java/Python to call .so
  3. If JSON:
    • Create `curses-theme-specs` repo with JSON definitions
    • Write JSON Schema for validation
    • Update both projects to load from JSON

References

  • Multi-AI analysis: 244K tokens, 92% consensus
  • Java FFM already proven in `NcursesBridge.java`
  • Both implementations have nearly identical `Theme3D` interfaces
  • Reference project: https://github.com/shmuelamar/rs-py-java

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions