Notcurses::Native ā Readme
NAME
Notcurses::Native - Complete NativeCall bindings for the notcurses TUI library
SYNOPSIS
use Notcurses::Native;
use Notcurses::Native::Types;
use Notcurses::Native::Plane;
# Initialize notcurses
my $nc = notcurses_init(NotcursesOptions.new, Pointer);
my $std = notcurses_stdplane($nc);
# Write colored text
ncplane_set_fg_rgb8($std, 0, 255, 128);
ncplane_putstr_yx($std, 0, 0, 'Hello from notcurses!');
notcurses_render($nc);
# Wait for input
my $ni = Ncinput.new;
notcurses_get_blocking($nc, $ni);
notcurses_stop($nc);
DESCRIPTION
Notcurses::Native provides complete 1:1 NativeCall bindings for notcurses v3.0.17, a modern terminal UI library supporting rich text, colors, images, video, and pixel-perfect rendering via Sixel and Kitty graphics protocols.
This module vendors notcurses and builds it from source, so no system installation of notcurses is required. FFmpeg is used for multimedia support (image/video loading).
606 functions are bound across 9 modules, covering 100% of the bindable
notcurses API. The only unbound functions are 4 vprintf variants that
take va_list, which cannot be bridged through any FFI. The five
printf-style functions (ncplane_printf, ncplane_printf_yx,
ncplane_printf_aligned, ncplane_printf_stained,
ncdirect_printf_aligned) keep their C names and arguments but are
Raku subs: NativeCall cannot call a C variadic function safely, so they
format with Raku's sprintf and write through notcurses's fixed-arity
calls. C formats work unchanged ā see ncplane_printf.
MODULES
Notcurses::Native
Core context management: init, stop, render, input, capabilities.
use Notcurses::Native;
my $nc = notcurses_init(NotcursesOptions.new, Pointer);
notcurses_render($nc);
my $ni = Ncinput.new;
my $key = notcurses_get_blocking($nc, $ni);
notcurses_stop($nc);
Key functions: notcurses_init, notcurses_stop, notcurses_render,
notcurses_stdplane, notcurses_get_blocking, notcurses_get_nblock,
notcurses_cantruecolor, notcurses_canutf8, notcurses_mice_enable.
Notcurses::Native::Types
All CStruct definitions, enums, constants, and opaque handle types.
CStruct types: NotcursesOptions, NcplaneOptions, Nccell, Ncinput, Ncstats, Nccapabilities, Ncvgeom, NcvisualOptions, Timespec, and all widget options structs (NcselectorOptions, NcmenuOptions, NctabbedOptions, NcplotOptions, NcprogbarOptions, NcreaderOptions, etc.). Their string fields and item arrays are owned by the struct ā see MEMORY OWNERSHIP. Every struct's layout and every constant's value is checked against the pinned notcurses headers by the test suite (t/42-abi-guard).
Enums: NcLogLevel, NcAlign, NcBlitter, NcScale, NcInputType, NcPixelImpl.
Key constants: 130 NCKEY_* key codes (NCKEY_UP, NCKEY_ESC, NCKEY_F01, NCKEY_BUTTON1, etc.), NCSTYLE_*, NCOPTION_*, NCALPHA_*, NCVISUAL_OPTION_*, NCMICE_*, NCBOX_*, NCKEY_MOD_*.
Notcurses::Native::Plane
133 plane functions: create, destroy, write text, read back, cursor, colors, styles, channels, box drawing, lines, gradients, merge, resize, reparent, z-ordering, and printf (formatted in Raku, C formats accepted).
use Notcurses::Native::Plane;
my $child = ncplane_create($std, NcplaneOptions.new(:rows(10), :cols(40)));
ncplane_set_fg_rgb8($child, 255, 0, 0);
ncplane_putstr_yx($child, 0, 0, 'Red text');
ncplane_rounded_box($child, 0, 0, 9, 39, 0);
ncplane_destroy($child);
Notcurses::Native::Cell
56 cell functions: load characters, get/set colors, styles, channels, alpha, palette index, duplicate, compare, box cell helpers.
use Notcurses::Native::Cell;
my $c = Nccell.new;
nccell_load($plane, $c, 'A');
nccell_set_fg_rgb($c, 0xFF0000);
nccell_set_styles($c, NCSTYLE_BOLD);
my $text = nccell_strdup($plane, $c);
nccell_release($plane, $c);
Notcurses::Native::Channel
60 channel functions: pure computation on 32-bit single channels and 64-bit dual channels. Set/get RGB, alpha, palette index, default flags. Also pixel (ABGR uint32) creation and component access.
use Notcurses::Native::Channel;
my uint64 $channels = 0;
ncchannels_set_fg_rgb($channels, 0xFF0000);
ncchannels_set_bg_rgb($channels, 0x0000FF);
my $reversed = ncchannels_reverse($channels);
Notcurses::Native::Context
55 functions: pile operations, palette management, capabilities queries, statistics, alignment, string width, fade context, metric formatting, system info.
Notcurses::Native::Direct
70 direct mode functions: simple terminal control without full-screen takeover. Colors, styles, cursor, box drawing, input, capabilities.
use Notcurses::Native::Direct;
my $ncd = ncdirect_core_init(Str, Pointer, 0);
ncdirect_set_fg_rgb8($ncd, 255, 0, 0);
ncdirect_putstr($ncd, 0, "Red text\n");
ncdirect_stop($ncd);
Notcurses::Native::Input
15 input query functions: modifier key predicates, key classification.
use Notcurses::Native::Input;
if ncinput_ctrl_p($ni) { say "Ctrl held" }
if nckey_synthesized_p($ni.id) { say "Synthesized key" }
if nckey_mouse_p($ni.id) { say "Mouse event" }
Notcurses::Native::Visual
23 visual/image functions: load from file, decode, resize, pixel manipulation, blit to planes, geometry queries.
use Notcurses::Native::Visual;
my $v = ncvisual_from_file('photo.png');
my $vopts = NcvisualOptions.new(:scaling(NCSCALE_SCALE), :blitter(NCBLIT_PIXEL));
$vopts.set-plane($std);
ncvisual_blit($nc, $v, $vopts);
ncvisual_destroy($v);
Notcurses::Native::Widgets
124 widget functions: progress bar, reel, selector, multiselector, tree, menu, tabbed, plot (uint64 and double), reader, FD plane, subprocess.
use Notcurses::Native::Widgets;
my $bar = ncprogbar_create($plane, NcprogbarOptions.new);
ncprogbar_set_progress($bar, 0.75e0);
ncprogbar_destroy($bar);
IMAGE VIEWING
Notcurses supports multiple rendering backends for images. On terminals that support it (Kitty, iTerm2), pixel-perfect rendering is available:
my $v = ncvisual_from_file('image.png');
# Check for pixel protocol support
my $pixel-ok = notcurses_check_pixel_support($nc);
my $blitter = $pixel-ok > 0 ?? NCBLIT_PIXEL
!! ncvisual_media_defblitter($nc, NCSCALE_SCALE);
my $plane = ncplane_create($std, NcplaneOptions.new(:rows($rows), :cols($cols)));
my $vopts = NcvisualOptions.new(:scaling(NCSCALE_SCALE), :blitter($blitter));
$vopts.set-plane($plane);
ncvisual_blit($nc, $v, $vopts);
INPUT HANDLING
loop {
my $ni = Ncinput.new;
notcurses_get_blocking($nc, $ni);
given $ni.id {
when NCKEY_UP { say "Up arrow" }
when NCKEY_DOWN { say "Down arrow" }
when NCKEY_ESC { last }
when NCKEY_ENTER { say "Enter" }
when NCKEY_F01 { say "F1" }
default { say "Key: {chr($ni.id)}" if $ni.id >= 32 }
}
if ncinput_ctrl_p($ni) { say " +Ctrl" }
if ncinput_shift_p($ni) { say " +Shift" }
}
MOUSE SUPPORT
notcurses_mice_enable($nc, NCMICE_ALL_EVENTS);
my $ni = Ncinput.new;
notcurses_get_blocking($nc, $ni);
if nckey_mouse_p($ni.id) {
say "Mouse at ({$ni.y}, {$ni.x})";
say "Button 1" if $ni.id == NCKEY_BUTTON1;
say "Scroll up" if $ni.id == NCKEY_SCROLL_UP;
}
notcurses_mice_disable($nc);
MEMORY OWNERSHIP
notcurses hands memory across the boundary in three different ways, and
each binding follows the one its C function uses. Where C gives the
caller something to free, this module offers a wrapper that copies the
data into Raku-owned storage and frees the original, so ordinary code
never calls free at all.
Library-owned (borrowed): never free
Static strings (notcurses_version, notcurses_str_blitter) and
pointers into a widget's own storage (ncselector_selected,
ncmenu_selected, nccell_extended_gcluster) are bound --> Str:
Raku copies the text and the original stays with notcurses.
ncpile_render_to_buffer also lends rather than gives, despite what
notcurses.h says: the frame lives in notcurses's own output buffer,
which it reuses on the next render and frees in notcurses_stop. The
frame is exactly buflen bytes with no NUL terminator. Use the
wrappers, which copy exactly the reported bytes:
use Notcurses::Native::Context;
my $std = notcurses_stdplane($nc);
ncplane_putstr_yx($std, 0, 0, 'hello');
# Byte-exact: diff it, hash it, or replay it to a terminal.
my buf8 $frame = ncpile-render-to-blob($std);
die 'render failed' without $frame;
$*OUT.write($frame);
# The same frame decoded as UTF-8 (strict: invalid UTF-8 dies).
my Str $text = ncpile-render-to-string($std);
Both answer their type object (buf8 / Str) when rendering fails,
and an empty frame when there is nothing to draw. The copy outlives
later frames and notcurses_stop.
Caller-owned: the wrapper frees for you
These C functions allocate for the caller. Each public name below copies the result into Raku-owned storage and frees the C allocation before returning:
ncplane_name($plane)ā the plane's name as aStr:''for a plane created without one, theStrtype object once cleared.ncplane-as-rgba($plane, $blit, $y, $x, $leny, $lenx, $pxy, $pxx)ā the region rasterised to abuf32of$pxy Ć $pxxpixels, red in the low byte; thebuf32type object when notcurses refuses.notcurses-stats-snapshot($nc, :$into)ā the current statistics as a Raku-ownedNcstats. Pass:intoto reuse one struct per frame.ncselector-destroy-selected($selector)ā destroys the selector and answers the option selected at that moment (Strtype object for a selector with no items).ncreader-destroy-contents($reader)ā destroys the reader and answers its text.ncplane_at_yx,ncplane_at_cursor,ncplane_contents,ncreader_contents,nccell_strdup,notcurses_at_yxand the other string returns ā already wrapped the same way.
use Notcurses::Native::Plane;
use Notcurses::Native::Context;
use Notcurses::Native::Widgets;
my ($h, $w);
with ncplane-as-rgba($plane, NCBLIT_1x1, 0, 0, 0, 0, $h, $w) -> $px {
say "{$h}x$w pixels; top-left is {$px[0].fmt('%08X')}";
}
my $stats = Ncstats.new;
loop {
render-frame();
notcurses-stats-snapshot($nc, :into($stats)); # no allocation per frame
last if $stats.renders > 1000;
}
my Str $choice = ncselector-destroy-selected($selector);
Strings and arrays you hand to notcurses
The option structs own the C strings in them. Pass a string to .new,
or replace it later with the struct's set-* method; either stores a
NUL-terminated UTF-8 copy that lives exactly as long as the struct, and
the accessor of the same name reads it back. notcurses copies what it
keeps during the call that receives the struct, so nothing else needs
to stay alive ā and one struct can be reused for any number of calls,
which matters because MoarVM never frees a CStruct's own memory.
my $opts = NcplaneOptions.new(:rows(1), :cols(20), :name('status'));
my $status = ncplane_create($std, $opts);
$opts.set-name('clock'); # the old copy is collectable
my $clock = ncplane_create($std, $opts);
Item arrays work the same way: give NcselectorOptions or
NcmultiselectorOptions a list of items, NcmenuSection a list of
NcmenuItems, or NcmenuOptions a list of sections, and the struct
builds the C array and owns it, strings and nested arrays included
(counts such as itemcount follow). A raw Pointer is still
accepted for an array you manage yourself.
my $menu = ncmenu_create($std, NcmenuOptions.new(:sections(
NcmenuSection.new(:name<File>, :items(
NcmenuItem.new(:desc<Open>),
NcmenuItem.new, # a separator
NcmenuItem.new(:desc<Quit>),
)),
)));
set-cstruct-str, which wrote a raw pointer into a struct and kept
every string it was ever given alive forever, is deprecated.
Raw bindings you free yourself
The raw forms stay exported for code that manages lifetimes itself:
ncplane_as_rgba answers a Pointer to free with c-free;
notcurses_stats_alloc answers an Ncstats to release with
notcurses-stats-free (only ever structs from that allocator ā never
one made with Ncstats.new); ncselector_destroy and
ncreader_destroy accept only NULL (Pointer) for their out-parameter.
borrowed-buf-from-pointer($ptr, $bytes) and strdup-copy-and-free
(from Notcurses::Native::Str) are the building blocks the wrappers use.
When you free a pointer yourself, pass the pointer, not a variable that
might have held the type object earlier: a NativeCall call site that
first receives an undefined value through a variable or an attribute (a
Scalar container) passes NULL ā or 0, for a boxed Int ā on every
later call through that site, so a free($maybe-null) in a loop
silently stops freeing once the first NULL goes through it. The same
goes for any raw binding handed a possibly-undefined Pointer, handle,
Str or Int from a variable. Guard the call, or decontainerise:
my Pointer $pixels = ncplane_as_rgba($plane, NCBLIT_1x1, 0, 0, 0, 0, $h, $w);
if $pixels {
# ... read the pixels ...
c-free($pixels<>);
}
BUILD REQUIREMENTS
Notcurses is vendored and built from source. You need:
CMake 3.21+ (what the pinned notcurses source requires)
A C compiler (gcc, clang, or mingw-w64 under MSYS2)
ncurses,libunistring,libdeflatedevelopment headersFFmpeg for image/video support (Linux, macOS, and Windows)
Linux (Debian / Ubuntu)
sudo apt install \
cmake pkg-config \
libncurses-dev libunistring-dev libdeflate-dev \
libavformat-dev libavcodec-dev libavdevice-dev \
libavutil-dev libswscale-dev
Fedora / RHEL equivalents:
sudo dnf install cmake pkgconf-pkg-config \
ncurses-devel libunistring-devel libdeflate-devel ffmpeg-devel
Arch / Manjaro equivalents:
sudo pacman -S cmake pkgconf base-devel \
ncurses libunistring libdeflate ffmpeg
Linux (openSUSE Tumbleweed)
openSUSE splits FFmpeg's libraries into per-component ffmpeg-7-*
packages. With thanks to user feedback from the Hacker News thread,
the verified minimum set is:
sudo zypper in cmake pkg-config gcc \
ncurses-devel libunistring-devel libdeflate-devel \
ffmpeg-7-libavcodec-devel ffmpeg-7-libavformat-devel \
ffmpeg-7-libavutil-devel ffmpeg-7-libavdevice-devel \
ffmpeg-7-libswscale-devel
libswresample, libavfilter, and libpostproc devel packages
are pulled in automatically as transitive dependencies; notcurses
itself doesn't link them directly.
macOS (Homebrew)
brew install cmake pkg-config ffmpeg ncurses libunistring libdeflate
Homebrew's ncurses is keg-only, so the build adds its pkgconfig
directory itself ā after any PKG_CONFIG_PATH you set, so yours wins ā
looking under $HOMEBREW_PREFIX, /opt/homebrew and /usr/local.
The libraries have to match the architecture Raku runs as: an x86_64
Rakudo under Rosetta on Apple Silicon needs x86_64 dependencies (an
x86_64 Homebrew under /usr/local, or a prefix of your own), not the
arm64 ones in /opt/homebrew. The build checks the ncurses keg's
architecture and passes over one that doesn't match, with a note saying
so, rather than linking it.
Windows (MSYS2 UCRT64)
Windows support requires MSYS2 in its UCRT64 environment ā this produces native Windows DLLs via mingw-w64 GCC. Visual Studio / MSVC are not supported. Upstream notcurses docs recommend OpenImageIO on Windows, but current MSYS2 OIIO (3.1.x) has ABI drift vs notcurses 3.0.17's oiio.cpp, so we use FFmpeg instead ā same media path as Linux/macOS.
Install MSYS2 from https://www.msys2.org/, open a UCRT64 shell, and:
pacman -S \
mingw-w64-ucrt-x86_64-cmake \
mingw-w64-ucrt-x86_64-ninja \
mingw-w64-ucrt-x86_64-toolchain \
mingw-w64-ucrt-x86_64-libdeflate \
mingw-w64-ucrt-x86_64-libunistring \
mingw-w64-ucrt-x86_64-ncurses \
mingw-w64-ucrt-x86_64-ffmpeg
Build tests (notcurses-tester) do not run on Windows ā upstream
limitation. The module builds and loads; terminal-dependent tests
(xt/) need to be run on Linux or macOS.
Core-only (no multimedia)
If you don't need image/video support, you can omit the FFmpeg dependency. The build detects missing multimedia libraries and falls back automatically.
INSTALLATION
zef install Notcurses::Native
On supported platforms this downloads a prebuilt self-contained archive from GitHub Releases, SHA256-verifies it against a checksum baked into the dist, and stages it into the user's XDG data dir. No system packages are touched. See PREBUILT BINARIES below for the platform matrix.
If you're on an unsupported platform, the build falls back to compiling notcurses from source via CMake ā see BUILD REQUIREMENTS above for the dev packages that needs.
Installation runs t/ tests only. None of them needs a terminal:
besides the pure-Raku checks (channel math, struct layouts, constants,
loader and packaging contracts), the memory-ownership tests start
notcurses in child processes with no terminal attached and assert on
how those children exit. The full terminal-dependent test suite lives
in xt/ and can be run manually:
prove -e 'raku -I lib' xt/*.rakutest
xxt/ holds checks that need tooling outside Raku. Today that is an
AddressSanitizer driver for the perf shim's cell copy, which needs a C
compiler with ASan (clang, or gcc with libasan; MSYS2 CLANG64 clang on
Windows) and the pinned notcurses headers, and fails rather than skips
without them:
prove6 -I lib xxt/
prove (Perl 5) is recommended for xt/ tests because
prove6 has a bug
where terminal escape sequences from C libraries corrupt its TAP
parser.
PREBUILT BINARIES
Each prebuilt archive contains the notcurses libraries plus every
non-system runtime dependency. macOS and Linux linker paths are relocated
to @loader_path and $ORIGIN; Windows keeps the complete DLL closure
flat and Notcurses::Native loads each entry point with that directory
explicitly enabled for dependency resolution. The binaries therefore find
each other inside the staged directory without touching host libraries.
Supported platforms
macOS arm64 (Apple Silicon, macOS 11.0 Big Sur and newer)
macOS x86_64 (Intel Mac / Hackintosh, macOS 10.15 Catalina and newer)
Linux x86_64 glibc ā manylinux_2_28 baseline (glibc 2.28; RHEL 8+ / Ubuntu 18.10+ / Debian 10+ / Fedora 28+ / Arch / Manjaro)
Linux aarch64 glibc ā same baseline
Linux x86_64 musl ā alpine:3.20 baseline (musl 1.20+; Alpine 3.13+ / Postmarket OS / Void / Adelie)
Linux aarch64 musl ā same baseline
Windows x86_64 ā mingw-w64 / UCRT
Windows arm64 ā clang / UCRT (not CI-verified; see below)
The CI release pipeline runs a codec capability probe against every
artefact before publish: dlopens the bundled libavcodec, confirms
the accelerated decoder libraries (libdav1d, libvpx,
libvpx-vp9, libopus) are registered, and actually decodes
PNG / JPEG / BMP fixtures end-to-end. A build with a misconfigured
or broken libavcodec doesn't reach the release.
Windows arm64 caveat: we build the prebuilt and ship it, but the
end-to-end Raku verify lane is currently disabled. Rakudo's
source-build path (rakubrew ā MoarVM) fails on Windows ARM64 MSYS2
CLANGARM64 ā NQP's Configure.pl probe trips on a perl-output
parse ā and setup-raku@v1 has no native Windows ARM64 prebuilt
yet, so there's no Rakudo to test against in CI. The bundle audit
on the build side (objdump-based import-table walk, sibling-DLL
self-containment check) still runs, so a broken bundle would still
fail the release. Users on Windows ARM64 are encouraged to report
issues.
Codec coverage
Every prebuilt bundles libavcodec configured with:
libdav1dā AV1 video decode, ~10Ć faster than ffmpeg's internal AV1 decoder for 4K contentlibvpxā VP8 / VP9 video decodelibopusā Opus audio decode
Image formats (PNG, JPEG, GIF, WebP, TIFF, BMP, etc.) and the other common video / audio codecs (H.264, HEVC, MPEG-4, MP3, AAC, Vorbis, FLAC, ā¦) use ffmpeg's internal decoders ā same code path on every platform.
Third-party licensing
A prebuilt archive is a binary redistribution, so every archive ships
its own THIRD-PARTY.md, a LICENSES/ directory and a
third-party.json next to the libraries. Between them they name
every file in that particular archive and the component it belongs
to (or, for a component statically linked into another library, the
files that contain it), each component's version, SPDX licence and
copyright notice, the exact upstream source it was built from
(tarball URL plus SHA-256, or a commit SHA), the build scripts at the
release's own tag, and the full text of every licence involved.
third-party.json is the machine-readable form, for tools that
bundle an archive into an application.
All three are generated from resources/third-party.json in this
repository, which doubles as a release gate: every file in an archive
must match a component listed there, and every component listed for
that platform must be present, or the build lane fails. A dependency
cannot arrive in a shipped archive without somebody having read its
licence first.
The bundled ffmpeg is a decoder-only build with neither
--enable-gpl nor --enable-nonfree, so it is conveyed under the
LGPL v2.1 or later. It, GNU libunistring and (in the Windows archives
only) GNU libiconv are the copyleft libraries in the archives; the
exact source tarballs they were built from are attached to every
binary release alongside the archives, and are covered by the same
checksums.txt. Everything else is permissive (Apache-2.0, BSD-2,
BSD-3, MIT, X11-style, Zlib). The Windows archives also carry the
mingw-w64 runtime, which the MSYS2 toolchains link statically into
every DLL, and winpthreads; their own notice files are in
LICENSES/. FFmpeg's JPEG decoder uses the Independent JPEG Group's
inverse DCT, so this software is based in part on the work of the
Independent JPEG Group; that notice ships in LICENSES/ too.
Platform C runtimes ā glibc, musl, Apple's /usr/lib and frameworks,
Windows' own DLLs ā are dynamically linked against whatever the user's
machine provides and are never bundled, so they are not redistributed
here at all.
Source-build fallback
For platforms outside the matrix (FreeBSD, OpenBSD, i686, riscv64,
ppc64le, ā¦) or when you explicitly set
NOTCURSES_NATIVE_BUILD_FROM_SOURCE=1, Notcurses::Native compiles
notcurses from source via CMake. That path needs the system packages
listed in BUILD REQUIREMENTS. The source build takes 5ā15 minutes
depending on the machine; the prebuilt download path is seconds.
Dependencies in a prefix of your own are found through the usual
variables, which the build passes on to CMake:
PKG_CONFIG_PATH (ffmpeg, ncurses), CMAKE_PREFIX_PATH
(libunistring, libdeflate) and, if you use it, LIBRARY_PATH ā whose
directories the build also records as run paths, so the installed
libraries find the dependencies they link without it at run time (on
Linux, those dependencies' own dependencies still need the loader's
usual search path, as for any program). Every install
configures from scratch, so after a failed attempt, correcting the
environment and installing again is enough; nothing CMake found the first
time is reused.
On Windows, explicitly add the active MSYS2 target bin directory (UCRT64
on x86_64, CLANGARM64 on arm64) to PATH when running an application against
a source-built install. Source builds retain ordinary MSYS2 DLL dependencies;
the installer records that provenance beside the staged libraries so the
runtime searches the DLL's own directory first and then the ordinary Windows
search path. A later PowerShell process does not inherit the build shell's
$MINGW_PREFIX/bin, so CI captures its Windows path with cygpath and
prepends that validated directory for the source-load probe. Published
prebuilts remain independent of MSYS2 PATH and are loaded only from their
closed sibling DLL set plus Windows system directories.
Environment knobs
NOTCURSES_NATIVE_BUILD_FROM_SOURCE=1ā skip the prebuilt download and always compile from source.NOTCURSES_NATIVE_BINARY_ONLY=1ā refuse to fall back to source build; fail loudly if no prebuilt is available for this platform. Useful in CI where source-build is undesired.NOTCURSES_NATIVE_BINARY_URL=I<url>ā override the GitHub Release base URL (point at a mirror).NOTCURSES_NATIVE_CACHE_DIR=I<path>ā override the cache for prebuilt downloads, fetched notcurses sources and compiled shims (default$XDG_CACHE_HOME, else~/.cache;%LOCALAPPDATA%on Windows).NOTCURSES_NATIVE_DATA_DIR=I<path>ā override the staged-libs directory (defaults to XDG_DATA_HOME).NOTCURSES_NATIVE_LIB_DIR=I<path>ā at runtime, load notcurses from this directory instead of the staged data dir (escape hatch for custom builds).
EXAMPLES
See the examples/ directory for complete working programs:
01-hello.rakuā Hello world02-colors.rakuā Color palette and style showcase03-boxes.rakuā Box drawing with nesting and colors04-input.rakuā Interactive keypress event viewer05-clock.rakuā Color-cycling real-time clock06-imgview.rakuā Terminal image viewer with pixel protocol support07-direct.rakuā Direct mode (no full-screen takeover)08-progress.rakuā Animated progress bar widgets
AUTHOR
Matt Doughty
COPYRIGHT AND LICENSE
Copyright 2026 Matt Doughty
This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.