20 KiB
Notcurses: blingful TUIs and character graphics
What it is: a library facilitating complex TUIs on modern terminal emulators, supporting vivid colors, multimedia, threads, and Unicode to the maximum degree possible. Things can be done with Notcurses that simply can't be done with NCURSES. It is furthermore fast as shit. What it is not: a source-compatible X/Open Curses implementation, nor a replacement for NCURSES on existing systems.
birthed screaming into this world by nick black (nickblack@linux.com)
- c++ wrappers by marek habersack (grendel@twistedcode.net)
- rust wrappers by José Luis Cruz (joseluis@andamira.net)
- python wrappers by igo95862 (igo95862@yandex.ru)
- zig wrappers by Jakub Dundalek (dundalek@gmail.com)
for more information, see dankwiki and the man pages. in addition, there is Doxygen output. there is a mailing list which can be reached via notcurses@googlegroups.com. i wrote a coherent guidebook, which is available for free download (or paperback purchase).
i've not yet added many documented examples, but src/poc/
and src/pocpp/
contain many small C and C++ programs respectively. notcurses-demo
covers
most of the functionality of Notcurses.
If you're running Notcurses applications in a Docker, please consult "Environment notes" below. If you need Notcurses on Ubuntu Focal (20.04 LTS), you can run:
sudo add-apt-repository ppa:dankamongmen/notcurses
sudo apt-get update
Introduction
Notcurses abandons the X/Open Curses API bundled as part of the Single UNIX Specification. For some necessary background, consult Thomas E. Dickey's superb and authoritative NCURSES FAQ. As such, Notcurses is not a drop-in Curses replacement.
Wherever possible, Notcurses makes use of the Terminfo library shipped with NCURSES, benefiting greatly from its portability and thoroughness.
Notcurses opens up advanced functionality for the interactive user on workstations, phones, laptops, and tablets, possibly at the expense of e.g. some industrial and retail terminals. Fundamentally, Curses assumes the minimum and allows you (with effort) to step up, whereas Notcurses assumes the maximum and steps down (by itself) when necessary. The latter approach probably breaks on some older hardware, but the former approach results in new software looking like old hardware.
Why use this non-standard library?
-
Thread safety, and efficient use in parallel programs, has been a design consideration from the beginning.
-
A more orderly surface than that codified by X/Open: Exported identifiers are prefixed to avoid common namespace collisions. Where reasonable,
static inline
header-only code is used. This facilitates compiler optimizations, and reduces loader time. Notcurses can be built without its multimedia functionality, requiring a significantly lesser set of dependencies. -
All APIs natively support the Universal Character Set (Unicode). The
nccell
API is based around Unicode's Extended Grapheme Cluster concept. -
Visual features including images, fonts, video, high-contrast text, sprites, and transparent regions. All APIs natively support 24-bit color, quantized down as necessary for the terminal.
-
It's Apache2-licensed in its entirety, as opposed to the drama in several acts that is the NCURSES license (the latter is summarized as "a restatement of MIT-X11").
Much of the above can be had with NCURSES, but they're not what NCURSES was designed for. On the other hand, if you're targeting industrial or critical applications, or wish to benefit from time-tested reliability and portability, you should by all means use that fine library.
Requirements
Minimum versions generally indicate the oldest version I've tested with; it may well be possible to use still older versions. Let me know of any successes!
- (build) CMake 3.14.0+ and a C11 compiler
- (OPTIONAL) (OpenImageIO, testing, C++ bindings): A C++17 compiler
- (build+runtime) From NCURSES: terminfo 6.1+
- (build+runtime) GNU libunistring 0.9.10+
- (build+runtime) GNU Readline 8.0+
- (OPTIONAL) (build+runtime) From QR-Code-generator: libqrcodegen 1.5.0+
- (OPTIONAL) (build+runtime) From FFmpeg: libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
- (OPTIONAL) (build+runtime) OpenImageIO 2.15.0+, requires C++
- (OPTIONAL) (testing) Doctest 2.3.5+
- (OPTIONAL) (documentation) pandoc 1.19.2+
- (OPTIONAL) (python bindings): Python 3.7+, CFFI 1.13.2+, pypandoc 1.5+
- (OPTIONAL) (rust bindings): rust 1.47.0+, bindgen 0.55.1+, pkg-config 0.3.18+, cty 0.2.1+
- (runtime) Linux 5.3+, FreeBSD 11+, or DragonFly BSD 5.9+
Here's more information on building and installation.
Included tools
Eight binaries are installed as part of Notcurses:
ncls
: anls
that displays multimedia in the terminalncneofetch
: a neofetch ripoffncplayer
: renders visual media (images/videos)nctetris
: a tetris clonenotcurses-demo
: some demonstration codenotcurses-info
: detect and print terminal capabilities/diagnosticsnotcurses-input
: decode and print keypressesnotcurses-tester
: unit testing
To run notcurses-demo
from a checkout, provide the data
directory via
the -p
argument. Demos requiring data files will otherwise abort. The base
delay used in notcurses-demo
can be changed with -d
, accepting a
floating-point multiplier. Values less than 1 will speed up the demo, while
values greater than 1 will slow it down.
notcurses-tester
likewise requires that data
, populated with the necessary
data files, be specified with -p
. It can be run by itself, or via make test
.
Documentation
With -DUSE_PANDOC=on
(the default), a full set of man pages and XHTML
will be built from doc/man
. The following Markdown documentation is included
directly:
- Per-release News for packagers, developers, and users.
- The
TERM
environment variable and various terminal emulators. - Notes on contributing and hacking.
- There's a semi-complete reference guide.
- A list of other TUI libraries.
- Abbreviated history and thanks.
- Differences from Curses and adapting Curses programs.
If you (understandably) want to avoid the large Pandoc stack, but still enjoy manual page goodness, I publish a tarball with generated man/XHTML along with each release. Download it, and install the contents as you deem fit.
Environment notes
-
If your
TERM
variable is wrong, or that terminfo definition is out-of-date, you're going to have a very bad time. Use onlyTERM
values appropriate for your terminal. If this variable is undefined, or Notcurses can't load the specified Terminfo entry, it will refuse to start, and you will not be going to space today. -
Ensure your
LANG
environment variable is set to a UTF8-encoded locale, and that this locale has been generated. This usually means"[language]_[Countrycode].UTF-8"
, i.e.en_US.UTF-8
. The first part (en_US
) ought exist as a directory or symlink in/usr/share/locales
. This usually requires editing/etc/locale.gen
and runninglocale-gen
. On Debian systems, this can be accomplished withdpkg-reconfigure locales
, and enabling the desired locale. The default locale is stored somewhere like/etc/default/locale
. -
If your terminal has an option about default interpretation of "ambiguous-width characters" (this is actually a technical term from Unicode), ensure it is set to Wide, not narrow (if that doesn't work, ensure it is set to Narrow, heh).
-
If your terminal supports 3x8bit RGB color via
setaf
andsetbf
(most modern terminals), but doesn't export thergb
terminfo capability, you can export theCOLORTERM
environment variable astruecolor
or24bit
. Note that some terminals accept a 24-bit specification, but map it down to fewer colors. RGB is enabled whenever Foot, Contour, WezTerm, or Kitty is identified.
Fonts
Fonts end up being a whole thing, little of which is pleasant. I'll write this up someday FIXME.
It is worth knowing that several terminals draw the block characters directly,
rather than loading them from a font. This is generally desirable. To inspect
your environment's rendering of drawing characters, run notcurses-info
.
FAQs
If things break or seem otherwise lackluster, please consult the
Environment Notes section! You need to have a correct
TERM
and LANG
definition, and probably want COLORTERM
.
-
Q: The demo fails in the middle of
intro
. A: Check that yourTERM
value is correct for your terminal.intro
does a palette fade, which is prone to breaking under incorrectTERM
values. If you're not usingxterm
, yourTERM
should not bexterm
! -
Q: Can I have Notcurses without this huge multimedia stack? A: Yes! Build with
-DUSE_MULTIMEDIA=none
. -
Q: Can I build this individual Notcurses program without aforementioned multimedia stack? A: Again yes! Use
notcurses_core_init()
orncdirect_core_init()
in place ofnotcurses_init()
/ncdirect_init()
, and link with-lnotcurses-core
. Your application will likely start a few milliseconds faster; more importantly, it will link against minimal Notcurses installations. -
Q: Notcurses looks like absolute crap in
screen
. A:screen
doesn't support RGB colors (at least as of 4.08.00); if you haveCOLORTERM
defined, you'll have a bad time. If you have ascreen
that was compiled with--enable-colors256
, try exportingTERM=screen-256color
as opposed toTERM=screen
. -
Q: Notcurses looks like absolute crap in
mosh
. A: Yeah it sure does. I'm not yet sure what's up. -
Q: Why didn't you just render everything to Sixel? A: That's not a TUI; it's a slow and inflexible GUI. Many terminal emulators don't support Sixel. Sixel doesn't work well with mouse selection. Sixel has a limited color palette. With that said, both Sixel and the Kitty bitmap protocol are well-supported.
-
Q: I'm not seeing
NCKEY_RESIZE
until I press some other key. A: You've almost certainly failed to maskSIGWINCH
in some thread, and that thread is receiving the signal instead of the thread which callednotcurses_getc_blocking()
. As a result, thepoll()
is not interrupted. Callpthread_sigmask()
before spawning any threads. -
Q: Using the C++ wrapper, how can I ensure that the
NotCurses
destructor is run when I return frommain()
? A: As noted in the C++ FAQ, wrap it in an artificial scope (this assumes yourNotCurses
is scoped tomain()
). -
Q: How do I hide a plane I want to make visible later? A: In order of least to most performant: move it offscreen using
ncplane_move_yx()
, move it underneath an opaque plane withncplane_move_below()
, or move it off-pile withncplane_reparent()
. -
Q: Why isn't there an
ncplane_box_yx()
? Do you hate orthogonality, you dullard? A:ncplane_box()
and friends already have far too many arguments, you monster. -
Q: Why doesn't Notcurses support 10- or 16-bit color? A: Notcurses supports 24 bits of color, spread across three eight-bit channels. You presumably mean 10-bit-per-channel color. I needed those six bits for other things. When terminals support it, Notcurses might support it.
-
Q: The name is dumb. A: That's not a question?
-
Q: I'm not finding qrcodegen on BSD, despite having installed
graphics/qr-code-generator
. A: Trycmake -DCMAKE_REQUIRED_INCLUDES=/usr/local/include
. This is passed bybsd.port.mk
. -
Q: Do you support musl? A: I try to! You'll need at least 1.20.
-
Q: I only seem to blit in ASCII, and/or can't emit Unicode beyond ASCII in general. A: Your
LANG
environment variable is underdefined or incorrectly defined, or the necessary locale is not present on your machine (it is also possible that you explicitly suppliedNCOPTION_INHIBIT_SETLOCALE
, but never calledsetlocale(3)
, in which case don't do that). -
Q: I pretty much always need an
ncplane
when using anccell
. Why doesn't the latter hold a pointer to the former? A: Besides the massive redundancy this would entail,nccell
needs to remain as small as possible, and you almost always have thencplane
handy if you've got a reference to a validnccell
anyway. -
Q: I ran
notcurses-demo
, but my table numbers don't match the Notcurses banner numbers, you charlatan. A:notcurses-demo
renders several frames beyond the actual demos. -
Q: When my program exits, I don't have a cursor, or text is invisible, or colors are weird, ad nauseam. A: Ensure you're calling
notcurses_stop()
/ncdirect_stop()
on all exit paths, including fatal signals (note that, by default, Notcurses installs handlers for most fatal signals to do exactly this). -
Q: How can I use Direct Mode in conjunction with libreadline? A: Pass
NCDIRECT_OPTION_CBREAK
toncdirect_init()
, and ensure you do not passNCDIRECT_OPTION_NO_READLINE
. If you'd like, setrl_readline_name
andrl_attempted_completion_function
prior to callingncdirect_init()
. With that said, consider using a Notcursesncreader
. -
Q: Will there ever be Java wrappers? A: I should hope not. If you want a Java solution, try Autumn Lamonte's Jexer.
-
Q: Given that the glyph channel is initialized as transparent for a plane, shouldn't the foreground and background be initialized as transparent, also? A: Probably (they are instead initialized to default opaque). This would change some of the most longstanding behavior of Notcurses, though, so it isn't happening.
-
Q: Why does my right-to-left text appear left-to-right? A: Notcurses doesn't honor the BiDi state machine, and in fact forces left-to-right with BiDi codes. Likewise, ultra-wide glyphs will have interesting effects. ﷽!
-
Q: I get linker errors when statically linking. A: Are you linking all necessary libraries? Use
pkg-config --static --libs notcurses
(or--libs notcurses-core
) to discover them. -
Q: Can I avoid manually exporting
COLORTERM=24bit
everywhere? A: Sure. AddSendEnv COLORTERM
to.ssh/config
, andAcceptEnv COLORTERM
tosshd_config
on the remote server. Yes, this will probably require root on the remote server. Don't blame me, man; I didn't do it. -
Q: How about arbitrary image manipulation here functionality? A: I'm not going to beat ImageMagick et al. on image manipulation, but you can load an
ncvisual
from RGBA memory usingncvisual_from_rgba()
. -
Q: My program locks up during initialization. A: Notcurses interrogates the terminal. If the terminal doesn't reply to standard interrogations, file a Notcurses bug, send upstream a patch, or use a different terminal. No known terminal emulators exhibit this behavior.
-
Q: How do I mix Rendered and Direct mode? A: You really don't want to. You can stream a subprocess to a plane with the
ncsubproc
widget.
Useful links
- BiDi in Terminal Emulators
- The Xterm FAQ
- The NCURSES FAQ
- ECMA-35 Character Code Structure and Extension Techniques (ISO/IEC 2022)
- ECMA-43 8-bit Coded Character Set Structure and Rules
- ECMA-48 Control Functions for Coded Character Sets (ISO/IEC 6429)
- Unicode 13.1 Full Emoji List
- Unicode Standard Annex #29 Text Segmentation
- Unicode Standard Annex #15 Normalization Forms
- The TTY demystified
- Dark Corners of Unicode
- UTF-8 Decoder Capability and Stress Test
- Emoji: how do you get from U+1F355 to 🍕?
- Glyph Hell: An introduction to glyphs, as used and defined in the FreeType engine
- My wiki's Sixel page and Kitty's extensions.
Useful man pages
- Linux: console_codes(4)
- Linux: termios(3)
- Linux: ioctl_tty(2)
- Linux: ioctl_console(2)
- Portable: terminfo(5)
- Portable: user_caps(5)
Grotesque vanity and meaningless metrics
“Our fine arts were developed, their types and uses were established, in times very different from the present, by men whose power of action upon things was insignificant in comparison with ours. But the amazing growth of our techniques, the adaptability and precision they have attained, the ideas and habits they are creating, make it a certainty that profound changes are impending in the ancient craft of the Beautiful.” —Paul Valéry