blingful character graphics/TUI library. definitely not curses.
by far the longest time between Notcurses releases, and there's not very much here, save a nasty infinite loop discovered and fixed by @drewt. is this project dead? let me answer that with dmx:
hell no this project isn't dead, but this project has achieved most of its primary goals, and i don't intend to move on longer-term, more speculative ones until next spring (2023) at the earliest. gotta make that paper in the meantime, and i'm deeply embroiled in the shit at work, keeping the satellites doing what they do best and generally doing the do as it's done. but this bugfix release did feel warranted.
ALSO, regarding inability to build against the most recent doctest 2.4.9, yeah it totally fucking sucks. it's a bug in doctest, and it was fixed months ago, and we just haven't had a new doctest release. I've asked for one; we'll see. Ugh. this isn't really something we can work around easily, and there's no reason i can see for doctest not dropping a fixed 2.4.10. if this continues, i might have to rethink reliance on doctest... =\
anyway! here you go, ya filthy animals
we haven't had as much notcurses lately as we were! well, after two and odd years going balls-to-the-wall on it, it had reached a point where i could finally cool off a bit, and i did! still absolutely resolving bugs and adding new features, just took some time to do other things. like build a new workstation and write a lengthy essay and regain favor at my day job, where terminal graphics absolutely do not pay the bills. but i still love ye, and we're still here.
this fixes use of bare Escape as input in terminals other than kitty, and recognizes curly underlines in Contour. some other bugs came in over this period, but most of them were resolved via external fixes, hurrah.
I made the horrible decision to upgrade and generally trick out my home workstation, and a seeming eternity later, I finally have it more or less back up, except it now prints something about "spurious native exception"s every ~3s, Never get into watercooling, kids.
\t
in Cese, ASCII/UCS 9 "Horizontal Tab")! Tabstops are relative to the output plane, and fixed at 8 cells (#1233)notcurses_render()
will now be called in the context of that output. This was done to work around a major annoyance and limitation of CLI mode, that you can't just dump output like one does in a ... CLI, and instead had to call notcurses_render()
explicitly a bunch if you wanted to actually generate all the output you wrote, and have it in the scrollback buffer etc. This was universally despised. So in a Alexanderish strike at this Gordian shit sandwich, we now call notcurses_render()
. This means you can just dump output to a scrolling standard plane, and not worry about anything except a final notcurses_render()
. Your output will be displayed as you write it, and all of it will be displayed.This mechanism is subject to change, but it's probably how things are working going forward.
"but dank" i hear you asking, "wouldn't this rule apply to all planes? why the special case?" the answer is no, and the reasoning is that you've chosen to engage a scrolling standard plane, which comes with a host of different behaviors. ineluctable modalities of the hackable, as stephen daedalus might say.
ncplane_puttext()
is used with NCALIGN_UNALIGNED
(#2611 ) , thanks @alexhsamuel for the report!ncmenu
(#2592), thanks @kascote for the report!ncmenu
entered an infinite loop when opening a menu with no enabled items (#2606)assert()
when replies came broken across multiple reads (#2590 ); this was especially nasty when running over ssh. You wouldn't see this bug unless your Notcurses was built with CMAKE_BUILD_TYPE=Debug
or NDEBUG
was explicitly undefined, thanks @christianparpart for the report!DECSDM
unless we know for sure what terminal we're talking to (#2587), thanks @autumnmeowmeow / @j4james / @dnkl for the report and discussion!ncvisual
, and encode in about 20%--50% of the 3.0.5 times. A tremendous improvement. How delightfully baller. We're still not where I'd like to be, but we're so much better than we have ever been. This work was tough, and dominated the 3.0.6 cycle (#2573 , #2603). #2573 is an epic story indeed, well worth reading IMHO.Welcome @alexhsamuel , who appears to be taking over the Python wrappers, aka the bane of my existence.
i'll be having some intestines removed next week, having deemed them unfit for my august personage, but i doubt it'll slow me down for more than a few hours. hack on!
I checked the guidebook and it said: Excellent food, malevolent ambience. I'd been habitually abusing an illegal growth hormone extracted from the pituitary glands of human corpses and I felt as if I were drowning in excremental filthiness but the prospect of having something good to eat cheered me up. I asked the waitress about the soup du jour and she said that it was primordial soup—which is ammonia and methane mixed with ocean water in the presence of lightning. Oh I'll take a tureen of that embryonic broth, I say, constraint giving way to exuberance—but as soon as she vanishes my spirit immediately sags because the ambience is so malevolent. The bouncers are hassling some youngsters who want drinks—instead of simply carding the kids, they give them radiocarbon tests, using traces of carbon 14 to determine how old they are—and also there's a young wise guy from Texas A&M at a table near mine who asks for freshly ground Rolaids on his fettuccine and two waiters viciously work him over with heavy bludgeon-sized pepper mills, so I get right back into my car and narcissistically comb my thick jet-black hair in the rearview mirror and I check the guidebook. There's an inn nearby—it's called Little Bo Peep's— its habitues are shepherds. And after a long day of herding, shearing, panpipe playing, muse invoking, and conversing in eclogues, it's Miller time, and Bo Peep's is packed with rustic swains who've left their flocks and sunlit, idealized arcadia behind for the more pungent charms of hard-core social intercourse. Everyone's favorite waitress is Kikugoro. She wears a pale-blue silk kimono and a brocade obi of gold and silver chrysanthemums with a small fan tucked into its folds, her face is painted and powdered to a porcelain white. A cowboy from south of the border orders a "Biggu Makku." But Kikugoro says, "This is not Makudonarudo." She takes a long cylinder of gallium arsenide crystal and slices him a thin wafer which she serves with soy sauce, wasabi, pickled ginger, and daikon. "Conducts electrons ten times faster than silicon... taste good, gaucho-san, you eat," she says, bowing.
For all my test images, Notcurses now encodes sixels as quickly or more quickly than libsixel, while producing output at least as good as libsixel (when the latter is run without dithering). This completes the drive to improve Sixel quantization described in the 3.0.0 release notes. Yay!
notcurses-info
colors its gradient based on colors 4--7 of the palette, allowing it to be "themed" #2568ncpalette_get()
#2565get_cursor_location()
get_cursor_location()
ncdirect_readline()
notcurses-demo
: stop trying to read after EOF to avoid hot loop #2547[a weary dank emerges from the codemines] This one had some rough bugs deep in the input layer to resolve. Hopefully they're all actually fixed. I think they are.
NCKEY_EOF
and implement it properly along all paths #2525pmouse
entry to notcurses-info
to see whether 1016 is available #2326.[reels]
demo, which otherwise looked stupid in the [fission]
demo for DFSG builds #2535XTVERSION
. good for them.BUILD_FFI_LIBS
#2519this was not a fun work cycle, not at all. but these were all deep bugs that needed be knocked out. work on input isn't sexy, and it's hard to show off, but when the chips are down, it's something that'll differentiate the players from the scrubs. ours is the most general, complex, and powerful of which i'm aware (would you expect anything less?), and input's something where a lot of terminals went their own ways.
anyway, hack on.
No big changes here. Sixel quantization has been rewritten; it is now substantially faster, but has taken a quality hit on some images. I'll be working on rectifying this for 3.0.4 (see #1452, #2503, and #2516).
[box]
demo when there is no available pixel geometry (#2505)there might have been a few other things in there, but they were small. Most work was related to improving SIxel quantization.
ncplayer -k
and the notcurses-demo
usage screen have been converted from Direct Mode to CLI mode (#2188, #2486)[highcontrast]
to [dragon]
(#2419)nccell_release()
on cells lost to ncplane_resize()
(#2426)NCALIGN_RIGHT
with overfull line resulted in no output (#2472)[view]
demo now keeps the picture-in-a-picture aligned with the right border on resize (#2479)-L
option for ncls
(dereference symlinks) (#2006)assert()
out of ncneofetch
when lacking a logo (#2494)tfman
, a man page browser ala man(1)
. Experimental.DFSG_BUILD
.NCPLANE_OPTION_VSCROLL
to enable scrolling on a plane from the beginning.NCPLANE_OPTION_AUTOGROW
to support autogrowth of planes
nctree
s are now dynamic. add nctree_del()
and nctree_add()
. you can now create an empty nctree
. #2458oc
on startup and shutdown, preserving custom palettes #2450ncplane_puttext()
#2446ncchannels_reverse()
to enter illegal states #2421ncplane_putc()
, as they could be invalidated within (fixes OfflineEGCs
unit test) #2420sigaltstack()
on UNIX #2424ncplane_resize()
when only the y dimension is changing #2425I keep it playa while some choose to play it safe boi, check the résumé: it's risky business in the 'A'
Notcurses 3.0.0 represents over a year of development since Notcurses 2.0.0 "Stankonia".
The overall vision remains unchanged from the very beginning—composition of z-ordered planes, with an API naturally expressed in terms of 888RGB DirectColor and Unicode Extended Grapheme Clusters, amenable to multithreading—but it's grown beyond all my dreams. Notcurses has pushed the terminal ecosystem forward, with patches and influence going into XTerm, Kitty, Alacritty, MLterm, Contour, foot, the Linux console, iTerm2, MSYS2, console-setup, Valgrind, musl, and some I'm forgetting. Notcurses makes use of advanced new kernel functionality like Linux's pidfds, but at the same time runs on various BSDs, macOS, and even Microsoft Windows (natively, not just through WSL). It fully supports the new Windows ConPTY and Microsoft Terminal. It can be used to build fixed-screen TUIs and scrolling CLIs. Aside from its lack of Curses compatibility, it is—as far as I'm aware—the best, most powerful, ballinest terminal graphics library in existence.
If you're writing a new terminal application in 202x, you probably ought be using Notcurses.
are you a python hacker with a solid understanding of wrapping C? because i absolutely am not, and honestly have no interest in becoming one. i desperately need someone committed to adapting new C functionality in python as it emerges, ideally further extending that to create a truly pythonesque Notcurses interface. pick up @igo95862 's work in python/
and go to town. do you write go? same shit! c#? dot-net this motherfucker! what's in it for you? i wish i knew the answer to that question myself. @joseluis seems to have the situation pretty well handled for rust; ask him. i'll be handling fortran, prolog, and threaded intercal wrappers.
left: a python eats a crocodile, before unhinging its dynamically-typed jaw and eating my time. note that the python is devouring the crocodile without a hint of parallelism, despite having seemingly evolved for, like, six hundred million years now. i bet it OOMs.
Notcurses 3.0.0 introduces a hard ABI break relative to previous versions, and changes the SONAME to reflect this. Previous binaries will not link against Notcurses 3.0.0+. Even if you use no new API, nor any API which has been deprecated, your binary must be recompiled. The API has expanded throughout 2.x development, but 3.0.0 removes all functionality previously only deprecated. See the API changenotes below for complete information. I'm sorry for the API changes, but they leave Notcurses much improved for the future. Most changes can be safely implemented via global regex.
There may be any number of additions to the API for Notcurses 4, but every attempt will be made to maintain backwards compatibility. The ABI will not change across 3.x development.
With that, the most major changes since 2.0.0 include:
TERM
.XTMODKEYS
and the Kitty keyboard protocol for rich events. Support for console mice via GPM. Note that the Kitty keyboard protocol is now supported by foot! Among other things, this allows disambiguation of key depress, repeat, and release, as well as detection of modifier keys by themselves. Clients can now specify which mouse events they want, and get mouse movement events even when no buttons are pressed.nctree
, ncprogbar
, and nctabbed
widgets, the latter from @MasFlam.ncvisual
s via various RGB formats, as requested by @kaniini .libnotcurses-core
has been split out from libnotcurses
, allowing binaries to be linked without the multimedia stack, even if the installed Notcurses was built with a multimedia stack.notcurses-info
binary provides a quick summation and exercise of terminal capabilities.ncls
binary lists and renders files.notcurses-view
has been renamed to ncplayer
, and now accepts -n
for non-interpolative scaling, and -a
to alpha-mask a color....but this is only a small sample of the expansions, improvements, and achievements of Notcurses 3.0.0! Give it a test drive. Write some apps around it. Hack. May one hundred flowers bloom! 百花齊放,百家爭鳴
868 issues were closed via 7.7K+ commits in this release cycle.
The first time I ran notcurses’ “jungle” demo in foot nothing happened. Turned out to be a bug in foot’s handling of color palette changes. This phenomenon has kept repeating itself; notcurses using features in novel ways unearths terminal emulator bugs. notcurses-demo turns your terminal emulator into a 90s computer demo—and that’s a good thing." —Daniel Eklöf (@dnkl), author of foot
I believe notcurses forms an excellent foundation for the next generation of terminal applications, with rich support for graphics and input, and lots of effort into graceful degradation for legacy terminals. I recommend anyone starting to build a terminal application in 2021 consider it as the foundation layer. —Kovid Goyal (@kovidgoyal), author of Kitty
Terminals have long been seen as an old era tech, and so, many applications retain the behavior of those apps and standard being even older than I am. Notcurses not so much! I am glad notcurses is trying to break out and breathe fresh air and new life into our beloved eco system again with many features the young generation of users and developers want and will expect. A huge thanks to Nick Black for helping us move forward in the terminal land. The notcurses demos are mind blowing and inspiring of what modern terminal applications can be capable of and I recommend anyone asking me what to use when building terminal apps to give notcurses a try. Hack on! —Christian Parpart (@christianparpart), author of Contour
The demo is great, and looks like it can push out enough detail to pull off silliness like pushing an SNES game’s output straight to the console. What might be the most impressive element of the library is that while it can blit high res graphics through a terminal emulator with graphical support, it will also work on the basic Linux console, with no graphical system installed, by using some very old tricks. I know what you’re wondering: That’s all well and good, but can it run Doom? Yep. —Hackaday 2021-05-20 "Terminal Magic with Notcurses"
Just ran
notcurses-demo
. Mind blowing. Is it even legal? —u/rmrfchik
Two Microsoft Terminals run ncneofetch
and ncplayer
. Microsoft Terminal does not (yet) support bitmap graphics, nor Unicode 13 (necessary for the sexblitter), so here we see the quadblitter.
Terminal.App, iTerm2, and Kitty 0.21.2 on macOS. iTerm2 does not yet support partially-transparent bitmap graphics, but its author is working on it. Terminal.App is a somewhat lackluster program.
WezTerm runs ncneofetch
while ssh'd into a FreeBSD 13 virtual machine. On the left, Kitty 0.21.3 churns through the [xray]
phase of notcurses-demo
.
In the beginning, there was struct notcurses
for a "Rendered Mode" context, and struct ncdirect
for a "Direct Mode" context. Among other differences, output never resulted in true, externally-meaningful screen scrolling in Rendered Mode (you could fake it internally with a scrolling plane, but this wouldn't e.g. scroll existing content you hadn't generated). Rendered Mode was geared towards fullscreen TUIs, and free-scrolling CLIs were only possible in Direct Mode.
Well, Direct Mode allows other programs to generate output, and thus never knows the screen contents, eliminating most optimizations. The two APIs diverged extensively, and you basically couldn't use any of the Notcurses widgets etc. with a CLI program. @klamonte's "The Command Line vs. the Terminal" speaks truth:
...users really need the "command line", but not so much the "terminal". What do I mean by that? By "command line", I mean pipe-able (composable) programs that read from stdin, write to stdout, and do a sequence of command / response / command / response. Just like the "read-eval-print-loop" (REPL) used by Lisp, Python, and many other programming languages.
We needed to unleash the full power of Rendered Mode on CLI-style applications. Hence "CLI mode", which uses the struct notcurses
context. It ought be initialized with NCOPTION_NO_CLEAR_BITMAPS
(to leave previous output unmolested), NCOPTION_PRESERVE_CURSOR
(to start wherever the program was launched), and NCOPTION_NO_ALTERNATE_SCREEN
(hopefully obvious). Upon acquiring the context, invoke ncplane_set_scrolling()
on the standard plane (it was not previously possible to enable scrolling on the standard plane). Congratulations, you now have a CLI app. You must continue to invoke notcurses_render()
or some other rasterizer to generate output.
I'm not yet satisfied with the CLI mode (see "Notcurses IV" below), but think its shortcomings can be remedied without any API/ABI breakage. In particular, it's currently too difficult to simply stream data and ensure you have it all in your scrollback history, due to all those manual render/raster calls. This will be improved.
If you've kept your application up to date through the 2.x.x development cycle, updating to eliminate deprecation warnings, congratulations! 3.0.0 introduces no user-visible changes relative to 2.4.x, save the removal of long-deprecated material. Some functions which were previously symbols in the library are now static inline
header-only implementations, and a recompile will be sufficient to get you linked and going. A recompile is in any case necessary due to struct
changes and other ABI variations.
Let's assume the worst plausible case: you have a program built against Notcurses 2.0.0, and want to build it against 3.0.0, retaining prior behavior. You'll probably need to make a change or two, most of them insubstantial:
notcurses
context alongside a direct mode ncdirect
context. If you were doing this to handle input, manage your cursor and use an ncreader
. If you were doing this to run a subprocess, use an ncsubproc
.ncvisual_render()
has been dropped in favor of ncvisual_blit()
. The latter responds to a NULL
target plane by creating a new pile, rather than creating a new plane in the standard pile. To get the old behavior, pass the standard plane as n
in ncvisual_options
, and add NCVISUAL_OPTION_CHILDPLANE
to flags
.cell_fchannel()
, cell_bchannel()
, cell_set_fchannel()
, and cell_set_bchannel()
. If you were using these, the safe parts can be accomplished with other existing functions. cell_width()
has been replaced by nccell_cols()
, and no longer requires an ncplane*
argument. All other functions starting with cell
now start with nccell
, and the cell
type is now nccell
. Macros starting with CELL
now start with NCCELL
.channel
now start with ncchannel
, and the channel
/channels
types are now ncchannel
/ncchannels
. Macros beginning with CHANNEL
now begin with NCCHANNEL
.palette
now start with ncpalette
. The palette256
type has been renamed ncpalette
.ncvisual_subtitle()
has been replaced with ncvisual_subtitle_plane()
, and returns a new ncplane*
.NCSTYLE_REVERSE
has been eliminated. ncchannels_reverse()
is available to reverse the fore- and background of an ncchannels
variable, but is not a direct replacement.NCSTYLE_DIM
has been eliminated. The same effect can be more portably achieved with channel RGB.NCSTYLE_BLINK
has been eliminated. The same effect can be more portably achieved with ncplane_pulse()
.renderfp
has been removed from notcurses_options
, with no replacement functionality.ncplane_rgba()
has been renamed ncplane_as_rgba()
.ncvisual_geom()
has been reworked to use the new ncvgeom
struct.ncplane_align()
has been replaced by ncplane_halign()
.notcurses_stats()
traded the const
modifier on its notcurses*
parameter for elimination of data races.ncplane_qrcode()
no longer accepts a blitter specification, since only NCBLIT_2x1
works.ncplane_put*()
functions now return the number of columns output, as documented (some were erroneously returning the number of bytes output).ncdirect_styles_{set, on, off}()
have been renamed ncdirect_{set, on, off}_styles()
.ncdirect_raster_frame()
no longer requires blitter
nor scale
.ncdirect_{fg, bg}_{default, rgb}()
have been renamed ncdirect_set_{fg, bg}_{default, rgb}()
.ncplane_options
lost its horiz
union, which was replaced with its x
member.The CLI mode came up kinda late in Notcurses design, well into the 2.x cycle. It became clear that streaming stdio CLIs are more frequently authored than high-performance, non-scrolling TUIs. By allowing the standard plane to be placed into scrolling mode, this functionality was bolted on. It's not where I want it. You currently have to manage all the scrolling yourself, which gets pretty hairy once you bring multiple planes into the picture. You furthermore need decide when to render, and no one wants to think about that. Render insufficiently frequently, and data will be lost to virtual internal scrolling; render every line, and you do a lot of extra work (CLI mode doesn't yet always take advantage of hardware scrolling). I intend to make fuller use of hardware scrolling so that rendering can take place on every virtual scroll event, for full fidelity, full performance, and no developer effort.
Notcurses has made use of Terminfo (as distributed with NCURSES) since its inception. The limitations of that library have become more and more apparent, and with Notcurses now using direct terminal queries wherever possible, it's time to consider ditching libterminfo (and perhaps even the TERM
environment variable, which can be—and all too often is—incorrect). Terminfo doesn't cover a substantial amount of behavior Notcurses actively employs, from bitmap graphics to extended input protocols. Many terminals in any case don't bother putting together an accurate terminfo database entry, simply calling themselves xterm
or xterm-256color
and approximating XTerm to one degree or another. Let us cast off this taint, and become taintless.
If you're a terminal author, I encourage you to implement the XTGETTCAP
and XTVERSION
control sequences.
It was obvious from the very first days of bitmap graphics support that breaking an image into cell-sized fragments offered many significant advantages. Performance issues of some terminals when making use of hundreds of independent graphics prevented me from immediately pursuing this implementation, but it's time to use it where possible. Mosaics will improve performance when interacting with other planes, improve reliability when the screen geometry changes, eliminate problems when a sprixel moves partially off-screen, and drastically simplify the logic of bitmapped graphics atop a terminal's matrix of cells.
My quantization algorithm is slower than libsixel, and yet manages to also produce lower-quality encodings (on the plus side, it's not known to be riddled with major security problems). I look forward to significant improvements in both regards, certainly to the level of libsixel. It's likely that we'll want to integrate some dithering capabilities as well. Here's another area where mosaics have benefits: when your sixels are only as large as a single cell, you're less likely to suffer from quantization effects (at the cost, of course, of bandwidth).
When using the Kitty graphics protocol, Notcurses is unparalleled.
The maintainer of libsixel has been missing for years. @ctrlcctrlv roped me into comaintainership of a fork earlier this year, and we've been patching up the worst of things, but it's a fundamentally insecure API. I'll be wrapping the Notcurses sixel code with a libsixel-like API, one existing applications can move to with simple changes (essentially, a length needs be passed in along with the source buffer).
This is some Deeper Fucking Magic from Beyond the Dawn of Time. Stay tuned to see something fundamentally new, if I can pull it off.
It's oftentimes (almost always, actually) the case that a palette-indexed encoding of a given frame can require less bandwidth than a pure RGB version, yet encode all colors losslessly (assuming it can freely fall back to RGB). Notcurses currently always uses RGB when it is available, and maps RGB to palette-indexed terminals in a naive fashion. We can instead come up with an optimal palette on the fly, so long as it can be done with sufficient performance (i.e. median cut on all cells of every frame is not going to cut it). Suitable algorithms seem pretty obvious, if perhaps non-trivial. Implement this to save bandwidth (the first-order determinant of overall performance), and also to improve image quality when rendering to non-RGB terminals, especially color-starved terminals like the Linux console.
Just when I added Linux framebuffer support, it gets well and truly obsoleted. DRM is the (relatively) new hotness. I started in on some support during this development cycle, but it proved an unexpectedly tremendous pain in my ass, and was punted down the road.