Repository: dankamongmen/notcurses (language: python)

Issue:
ncneofetch launched near but not at the bottom barfs infoplane atop logo
With #2375 getting merged, almost all `ncneofetch` runs are perfect. The one case that still fucks up is when we launch close to, but not at, the last line. In this case, we don't scroll up the palette and logo planes enough, and print the infoplane atop them.

I thought about how to fix this, and it's all about changing how we invoke external scrolling. I'll put the solution down here or probably just code it up.

I'd like to solve this for 3.0, but I'm a bit tired of fucking around with this scrolling shit, so who knows.

prepare for 3.0.0 release
terminal test set: XTerm, alacritty, kitty, contour, wezterm, xfce4-terminal, microsoft terminal, iterm2, foot, linux framebuffer

* [x] set installforge compression back to high for windows installer
* [x] 2.4.999 trial run on windows, make sure installer sets up working registry key
* [x] do an ASAN run over `notcurses-demo` in all terminals
* [x] do an ASAN run over `notcurses-demo -k` in all terminals
* [x] do an ASAN+OIIO run over `notcurses-demo` on all operating systems
* [x] do an ASAN run over `env LANG=C notcurses-demo` in all terminals
* [x] scan-build on freebsd and linux
* [x] do an ASAN run over `notcurses-tester` in all terminals
* [x] do an ASAN+OIIO run over `notcurses-tester` on all operating systems
* [ ] `ncplayer` with each scaling mode in square, wide, and tall terminal geometries on all terminals
* [ ] `ncplayer -k` with each scaling mode in square, wide, and tall terminal geometries on all terminals
* [x] play a game of `nctetris` on each operating system
* [ ] run all PoC/PoCPP binaries
* [x] run `notcurses-input` in all terminals
* [x] run `notcurses-info` in all terminals
* [ ] run `notcurses-info > /dev/null < /dev/null` in all terminals
* [ ] run `notcurses-info 2> /dev/null < /dev/null` in all terminals
* [x] run `bitmapstate` with a critical eye on each terminal
* [ ] verify that all versions switch to 3.0.0
* [ ] verify that SONAME changes
* [ ] cut that fucker

anyone reading this, please please please help test the current master!


Discussion / hints:
on xterm, we seem to print the top infoplane row on the bottom.
there are really only four cases:

* alternate screen: no need to scroll, nor position cursor. when we exit, we're placed back exactly where we ought be, and the content we've drawn is hidden.
* no alternate screen, no scrolling: place cursor at the furthest point to which we've written, never scroll
* no alt, scrolling, case 1: we write to the standard plane at either the last cell, or placing a newline on the last line. this forces a (physical) scroll. emit the actual newline for the latter case; in the former case, just ride.
* no alt, scrolling, case 2: we write to some other plane *n* lines beyond the bottom of the standard plane. maybe we want a scroll, maybe we don't. it must be indicated by a client call to `scroll_to_plane()` or whatnot. this is effected by executing *n* physical scrolls prior to drawing.

in both of these last two cases, the cursor ought always be finally placed on the last line, but *never causing a scroll in so doing*. scrolling happens *before* drawing. cursor finalization happens *after*, and never the twain shall meet.


Fix patch:
diff --git a/CMakeLists.txt b/CMakeLists.txt
index da24d65ca0..e6be5b4e85 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
 # 3.14.0 introduced NAME_WLE
 cmake_minimum_required(VERSION 3.14.0)
-project(notcurses VERSION 2.4.99
+project(notcurses VERSION 2.4.999
   DESCRIPTION "Blingful UI for modern terminal emulators"
   HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
   LANGUAGES C)
diff --git a/README.md b/README.md
index f5ff9b4cf8..a360841192 100644
--- a/README.md
+++ b/README.md
@@ -485,7 +485,7 @@ If things break or seem otherwise lackluster, **please** consult the
 
 <details>
   <summary>Will there ever be Java wrappers?</summary>
-  I should hope not. If you want a Java solution, try Autumn Lamonte's
+  I should hope not. If you want a Java solution, try @klamonte's
   <a href="https://jexer.sourceforge.io/">Jexer</a>. Autumn's a good
   woman, and thorough. We seem to have neatly partitioned the language
   space.
diff --git a/cffi/notcurses-pydemo.1.md b/cffi/notcurses-pydemo.1.md
index 60a9e5e00d..b73e1ebff8 100644
--- a/cffi/notcurses-pydemo.1.md
+++ b/cffi/notcurses-pydemo.1.md
@@ -1,6 +1,6 @@
 % notcurses-pydemo(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/cffi/setup.py b/cffi/setup.py
index 55458398cd..a93c9d0b50 100644
--- a/cffi/setup.py
+++ b/cffi/setup.py
@@ -49,7 +49,7 @@ def run(self):
 
 setup(
     name="notcurses",
-    version="2.4.99",
+    version="2.4.999",
     packages=['notcurses'],
     scripts=['notcurses-pydemo', 'ncdirect-pydemo'],
     package_dir={'': 'src'},
diff --git a/doc/Doxyfile b/doc/Doxyfile
index 83ef4d3f6e..a21ec4a88e 100644
--- a/doc/Doxyfile
+++ b/doc/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME           = Notcurses
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 2.4.99
+PROJECT_NUMBER         = 2.4.999
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
diff --git a/doc/man/index.html b/doc/man/index.html
index 0505b28b0b..f1fafadc79 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -38,7 +38,7 @@
    </a>
   </div>
   <iframe align="right" width="560" height="315" src="https://www.youtube.com/embed/dcjkezf1ARY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
-  <h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.4.99">v2.4.99</a>)</h2>
+  <h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.4.999">v2.4.999</a>)</h2>
   <a href="notcurses.3.html">notcurses(3)</a>—a blingful TUI library<br/>
   <h3>Executables (section 1)</h3>
   <a href="ncls.1.html">ncls</a>—list files, displaying multimedia along with them<br/>
diff --git a/doc/man/man1/ncls.1.md b/doc/man/man1/ncls.1.md
index ea05871fab..43836c9620 100644
--- a/doc/man/man1/ncls.1.md
+++ b/doc/man/man1/ncls.1.md
@@ -1,6 +1,6 @@
 % ncls(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/ncneofetch.1.md b/doc/man/man1/ncneofetch.1.md
index 03a95a28a2..cc97aafa9a 100644
--- a/doc/man/man1/ncneofetch.1.md
+++ b/doc/man/man1/ncneofetch.1.md
@@ -1,6 +1,6 @@
 % ncneofetch(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/ncplayer.1.md b/doc/man/man1/ncplayer.1.md
index 9f5c60d535..674080d746 100644
--- a/doc/man/man1/ncplayer.1.md
+++ b/doc/man/man1/ncplayer.1.md
@@ -1,6 +1,6 @@
 % ncplayer(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/nctetris.1.md b/doc/man/man1/nctetris.1.md
index 46a8d24588..b206fc0a1b 100644
--- a/doc/man/man1/nctetris.1.md
+++ b/doc/man/man1/nctetris.1.md
@@ -1,6 +1,6 @@
 % nctetris(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/notcurses-demo.1.md b/doc/man/man1/notcurses-demo.1.md
index 171baaa11e..1d50f57581 100644
--- a/doc/man/man1/notcurses-demo.1.md
+++ b/doc/man/man1/notcurses-demo.1.md
@@ -1,6 +1,6 @@
 % notcurses-demo(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/notcurses-info.1.md b/doc/man/man1/notcurses-info.1.md
index 682abf1829..ea4a1e06c0 100644
--- a/doc/man/man1/notcurses-info.1.md
+++ b/doc/man/man1/notcurses-info.1.md
@@ -1,6 +1,6 @@
 % notcurses-info(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man1/notcurses-input.1.md b/doc/man/man1/notcurses-input.1.md
index c08fe26634..45d124c3a5 100644
--- a/doc/man/man1/notcurses-input.1.md
+++ b/doc/man/man1/notcurses-input.1.md
@@ -1,6 +1,6 @@
 % notcurses-input(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md
index 2ecfc9ca84..ec95ab1049 100644
--- a/doc/man/man3/notcurses.3.md
+++ b/doc/man/man3/notcurses.3.md
@@ -1,6 +1,6 @@
 % notcurses(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_capabilities.3.md b/doc/man/man3/notcurses_capabilities.3.md
index 598ab8d2ce..240da2a03f 100644
--- a/doc/man/man3/notcurses_capabilities.3.md
+++ b/doc/man/man3/notcurses_capabilities.3.md
@@ -1,6 +1,6 @@
 % notcurses_capabilities(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_cell.3.md b/doc/man/man3/notcurses_cell.3.md
index 60ff9714ff..7971394c4b 100644
--- a/doc/man/man3/notcurses_cell.3.md
+++ b/doc/man/man3/notcurses_cell.3.md
@@ -1,6 +1,6 @@
 % notcurses_cell(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_channels.3.md b/doc/man/man3/notcurses_channels.3.md
index 84f1614bf6..1644f53763 100644
--- a/doc/man/man3/notcurses_channels.3.md
+++ b/doc/man/man3/notcurses_channels.3.md
@@ -1,6 +1,6 @@
 % notcurses_channels(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_core.3.md b/doc/man/man3/notcurses_core.3.md
index b7928b3e2d..eb72955aca 100644
--- a/doc/man/man3/notcurses_core.3.md
+++ b/doc/man/man3/notcurses_core.3.md
@@ -1,6 +1,6 @@
 % notcurses_core(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_direct.3.md b/doc/man/man3/notcurses_direct.3.md
index 234a9ae48d..d469efc8ba 100644
--- a/doc/man/man3/notcurses_direct.3.md
+++ b/doc/man/man3/notcurses_direct.3.md
@@ -1,6 +1,6 @@
 % notcurses_direct(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_fade.3.md b/doc/man/man3/notcurses_fade.3.md
index 08212898ec..23737b0b0e 100644
--- a/doc/man/man3/notcurses_fade.3.md
+++ b/doc/man/man3/notcurses_fade.3.md
@@ -1,6 +1,6 @@
 % notcurses_fade(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_fds.3.md b/doc/man/man3/notcurses_fds.3.md
index 435ab9a096..b6bb0c8f8e 100644
--- a/doc/man/man3/notcurses_fds.3.md
+++ b/doc/man/man3/notcurses_fds.3.md
@@ -1,6 +1,6 @@
 % notcurses_fds(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_init.3.md b/doc/man/man3/notcurses_init.3.md
index 30817ef98e..eb077f7c2b 100644
--- a/doc/man/man3/notcurses_init.3.md
+++ b/doc/man/man3/notcurses_init.3.md
@@ -1,6 +1,6 @@
 % notcurses_init(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_input.3.md b/doc/man/man3/notcurses_input.3.md
index 902abc0a1c..8e6d3a5007 100644
--- a/doc/man/man3/notcurses_input.3.md
+++ b/doc/man/man3/notcurses_input.3.md
@@ -1,6 +1,6 @@
 % notcurses_input(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_lines.3.md b/doc/man/man3/notcurses_lines.3.md
index 2b1655cd0f..e32ca5ce66 100644
--- a/doc/man/man3/notcurses_lines.3.md
+++ b/doc/man/man3/notcurses_lines.3.md
@@ -1,6 +1,6 @@
 % notcurses_lines(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_menu.3.md b/doc/man/man3/notcurses_menu.3.md
index 8c7f6a71c1..807a12db44 100644
--- a/doc/man/man3/notcurses_menu.3.md
+++ b/doc/man/man3/notcurses_menu.3.md
@@ -1,6 +1,6 @@
 % notcurses_menu(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_metric.3.md b/doc/man/man3/notcurses_metric.3.md
index 2d470c99e9..3476a85bbd 100644
--- a/doc/man/man3/notcurses_metric.3.md
+++ b/doc/man/man3/notcurses_metric.3.md
@@ -1,6 +1,6 @@
 % notcurses_metric(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_multiselector.3.md b/doc/man/man3/notcurses_multiselector.3.md
index 696a52b354..37cc3164d8 100644
--- a/doc/man/man3/notcurses_multiselector.3.md
+++ b/doc/man/man3/notcurses_multiselector.3.md
@@ -1,6 +1,6 @@
 % notcurses_multiselector(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_output.3.md b/doc/man/man3/notcurses_output.3.md
index db556acd26..c35314fc76 100644
--- a/doc/man/man3/notcurses_output.3.md
+++ b/doc/man/man3/notcurses_output.3.md
@@ -1,6 +1,6 @@
 % notcurses_output(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_palette.3.md b/doc/man/man3/notcurses_palette.3.md
index 5ec922c208..f442969895 100644
--- a/doc/man/man3/notcurses_palette.3.md
+++ b/doc/man/man3/notcurses_palette.3.md
@@ -1,6 +1,6 @@
 % notcurses_palette(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_pile.3.md b/doc/man/man3/notcurses_pile.3.md
index a0feea170e..3c91ec998b 100644
--- a/doc/man/man3/notcurses_pile.3.md
+++ b/doc/man/man3/notcurses_pile.3.md
@@ -1,6 +1,6 @@
 % notcurses_pile(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md
index 90840c2363..0bcf382258 100644
--- a/doc/man/man3/notcurses_plane.3.md
+++ b/doc/man/man3/notcurses_plane.3.md
@@ -1,6 +1,6 @@
 % notcurses_plane(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_plot.3.md b/doc/man/man3/notcurses_plot.3.md
index 24f2e21bc6..256be92942 100644
--- a/doc/man/man3/notcurses_plot.3.md
+++ b/doc/man/man3/notcurses_plot.3.md
@@ -1,6 +1,6 @@
 % notcurses_plot(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_reader.3.md b/doc/man/man3/notcurses_reader.3.md
index 57cffb13e4..de193e9962 100644
--- a/doc/man/man3/notcurses_reader.3.md
+++ b/doc/man/man3/notcurses_reader.3.md
@@ -1,6 +1,6 @@
 % notcurses_reader(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_reel.3.md b/doc/man/man3/notcurses_reel.3.md
index 7ce7eaafac..8732bfae27 100644
--- a/doc/man/man3/notcurses_reel.3.md
+++ b/doc/man/man3/notcurses_reel.3.md
@@ -1,6 +1,6 @@
 % notcurses_reel(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_refresh.3.md b/doc/man/man3/notcurses_refresh.3.md
index a374a7a822..c6b102c972 100644
--- a/doc/man/man3/notcurses_refresh.3.md
+++ b/doc/man/man3/notcurses_refresh.3.md
@@ -1,6 +1,6 @@
 % notcurses_refresh(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_render.3.md b/doc/man/man3/notcurses_render.3.md
index f00d0a48f4..fd7db7ff20 100644
--- a/doc/man/man3/notcurses_render.3.md
+++ b/doc/man/man3/notcurses_render.3.md
@@ -1,6 +1,6 @@
 % notcurses_render(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_selector.3.md b/doc/man/man3/notcurses_selector.3.md
index 9844aa4ff0..b026064586 100644
--- a/doc/man/man3/notcurses_selector.3.md
+++ b/doc/man/man3/notcurses_selector.3.md
@@ -1,6 +1,6 @@
 % notcurses_selector(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_stats.3.md b/doc/man/man3/notcurses_stats.3.md
index 6764fe13dc..1a6bab3c79 100644
--- a/doc/man/man3/notcurses_stats.3.md
+++ b/doc/man/man3/notcurses_stats.3.md
@@ -1,6 +1,6 @@
 % notcurses_stats(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_stdplane.3.md b/doc/man/man3/notcurses_stdplane.3.md
index 61cc5f255e..dbf976136d 100644
--- a/doc/man/man3/notcurses_stdplane.3.md
+++ b/doc/man/man3/notcurses_stdplane.3.md
@@ -1,6 +1,6 @@
 % notcurses_stdplane(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_stop.3.md b/doc/man/man3/notcurses_stop.3.md
index 6f87136599..385d0d8460 100644
--- a/doc/man/man3/notcurses_stop.3.md
+++ b/doc/man/man3/notcurses_stop.3.md
@@ -1,6 +1,6 @@
 % notcurses_stop(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_tabbed.3.md b/doc/man/man3/notcurses_tabbed.3.md
index cbbb1cb7cd..4de924f7f7 100644
--- a/doc/man/man3/notcurses_tabbed.3.md
+++ b/doc/man/man3/notcurses_tabbed.3.md
@@ -1,5 +1,5 @@
 % notcurses_tabbed(3)
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_tree.3.md b/doc/man/man3/notcurses_tree.3.md
index 38221680e7..c4bdbed035 100644
--- a/doc/man/man3/notcurses_tree.3.md
+++ b/doc/man/man3/notcurses_tree.3.md
@@ -1,6 +1,6 @@
 % notcurses_tree(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_util.3.md b/doc/man/man3/notcurses_util.3.md
index 1a0a814733..4fb8a417d0 100644
--- a/doc/man/man3/notcurses_util.3.md
+++ b/doc/man/man3/notcurses_util.3.md
@@ -1,6 +1,6 @@
 % notcurses_util(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 
diff --git a/doc/man/man3/notcurses_visual.3.md b/doc/man/man3/notcurses_visual.3.md
index 1d199b0ff6..f259c0696b 100644
--- a/doc/man/man3/notcurses_visual.3.md
+++ b/doc/man/man3/notcurses_visual.3.md
@@ -1,6 +1,6 @@
 % notcurses_visual(3)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME
 notcurses_visual - notcurses multimedia
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index bbfbd23d03..7ca5abf9cf 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -2951,6 +2951,22 @@ ncplane_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
   return ret;
 }
 
+static inline int
+ncplane_ascii_box(struct ncplane* n, uint16_t styles, uint64_t channels,
+                  unsigned ylen, unsigned xlen, unsigned ctlword){
+  int ret = 0;
+  nccell ul = NCCELL_TRIVIAL_INITIALIZER, ur = NCCELL_TRIVIAL_INITIALIZER;
+  nccell ll = NCCELL_TRIVIAL_INITIALIZER, lr = NCCELL_TRIVIAL_INITIALIZER;
+  nccell hl = NCCELL_TRIVIAL_INITIALIZER, vl = NCCELL_TRIVIAL_INITIALIZER;
+  if((ret = nccells_ascii_box(n, styles, channels, &ul, &ur, &ll, &lr, &hl, &vl)) == 0){
+    ret = ncplane_box(n, &ul, &ur, &ll, &lr, &hl, &vl, ylen, xlen, ctlword);
+  }
+  nccell_release(n, &ul); nccell_release(n, &ur);
+  nccell_release(n, &ll); nccell_release(n, &lr);
+  nccell_release(n, &hl); nccell_release(n, &vl);
+  return ret;
+}
+
 static inline int
 ncplane_perimeter_double(struct ncplane* n, uint16_t stylemask,
                          uint64_t channels, unsigned ctlword){
diff --git a/python/setup.py b/python/setup.py
index e521e4f3d5..db848f961e 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -38,7 +38,7 @@
 
 setup(
     name="notcurses",
-    version="2.4.99",
+    version="2.4.999",
     packages=['notcurses'],
     ext_modules=[
         Extension(
diff --git a/src/demo/demo.c b/src/demo/demo.c
index 93de71258a..f8b0dadd2a 100644
--- a/src/demo/demo.c
+++ b/src/demo/demo.c
@@ -326,21 +326,29 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
 }
 
 static int
-table_segment_color(struct ncplane* n, const char* str, const char* delim, unsigned color){
+table_segment_color(struct ncplane* n, const char* str, const char* delim,
+                    const char* ascdelim, unsigned color){
   ncplane_set_fg_rgb(n, color);
   if(ncplane_putstr(n, str) < 0){
     return -1;
   }
   ncplane_set_fg_rgb8(n, 178, 102, 255);
-  if(ncplane_putstr(n, delim) < 0){
-    return -1;
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    if(ncplane_putstr(n, delim) < 0){
+      return -1;
+    }
+  }else{
+    if(ncplane_putstr(n, ascdelim) < 0){
+      return -1;
+    }
   }
   return 0;
 }
 
 static int
-table_segment(struct ncplane* n, const char* str, const char* delim){
-  return table_segment_color(n, str, delim, 0xffffff);
+table_segment(struct ncplane* n, const char* str, const char* delim,
+              const char* ascdelim){
+  return table_segment_color(n, str, delim, ascdelim, 0xffffff);
 }
 
 static int
@@ -387,14 +395,21 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
   // FIXME this shouldn't be necessary, but without it, late in 2.4.x we
   // stopped printing the table header. see #2389.
   notcurses_render(nc);
-  table_segment(n, "             runtime", "│");
-  table_segment(n, " frames", "│");
-  table_segment(n, "output(B)", "│");
-  table_segment(n, "    FPS", "│");
-  table_segment(n, "%r", "│");
-  table_segment(n, "%a", "│");
-  table_segment(n, "%w", "│");
-  table_segment(n, "TheoFPS", "║\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n");
+  const char* sep;
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    sep = u8"│";
+  }else{
+    sep = "|";
+  }
+  table_segment(n, "             runtime", "│", "|");
+  table_segment(n, " frames", "│", "|");
+  table_segment(n, "output(B)", "│", "|");
+  table_segment(n, "    FPS", "│", "|");
+  table_segment(n, "%r", "│", "|");
+  table_segment(n, "%a", "│", "|");
+  table_segment(n, "%w", "│", "|");
+  table_segment(n, "TheoFPS", "║\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n",
+                "|\n--+--------+--------+-------+---------+-------+--+--+--+-------|\n");
   char timebuf[NCPREFIXSTRLEN + 1];
   char tfpsbuf[NCPREFIXSTRLEN + 1];
   char totalbuf[NCBPREFIXSTRLEN + 1];
@@ -423,21 +438,24 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
     ncplane_set_fg_rgb(n, rescolor);
     ncplane_printf(n, "%2llu", (unsigned long long)(i + 1)); // windows has %zu problems
     ncplane_set_fg_rgb8(n, 178, 102, 255);
-    ncplane_putwc(n, L'│');
+    ncplane_putegc(n, sep, NULL);
     ncplane_set_fg_rgb(n, rescolor);
     ncplane_printf(n, "%8s", demos[results[i].selector - 'a'].name);
     ncplane_set_fg_rgb8(n, 178, 102, 255);
-    ncplane_printf(n, "│%*ss│%7" PRIu64 "│%*s│%7.1f│%2" PRId64 "│%2" PRId64 "│%2" PRId64 "│%*s║",
-           NCPREFIXFMT(timebuf), results[i].stats.renders, NCBPREFIXFMT(totalbuf),
+    ncplane_printf(n, "%s%*ss%s%7" PRIu64 "%s%*s%s%7.1f%s%2" PRId64 "%s%2" PRId64 "%s%2" PRId64 "%s%*s%s",
+           sep, NCPREFIXFMT(timebuf), sep,
+           results[i].stats.renders, sep,
+           NCBPREFIXFMT(totalbuf), sep,
            results[i].timens ?
-            results[i].stats.renders / ((double)results[i].timens / NANOSECS_IN_SEC) : 0.0,
+            results[i].stats.renders / ((double)results[i].timens / NANOSECS_IN_SEC) : 0.0, sep,
            (results[i].timens ?
-            results[i].stats.render_ns * 100 / results[i].timens : 0),
+            results[i].stats.render_ns * 100 / results[i].timens : 0), sep,
            (results[i].timens ?
-            results[i].stats.raster_ns * 100 / results[i].timens : 0),
+            results[i].stats.raster_ns * 100 / results[i].timens : 0), sep,
            (results[i].timens ?
-            results[i].stats.writeout_ns * 100 / results[i].timens : 0),
-           NCPREFIXFMT(tfpsbuf));
+            results[i].stats.writeout_ns * 100 / results[i].timens : 0), sep,
+           NCPREFIXFMT(tfpsbuf),
+           notcurses_canutf8(ncplane_notcurses(n)) ? "║" : "|");
     ncplane_set_fg_rgb(n, rescolor);
     ncplane_printf(n, "%s\n", results[i].result < 0 ? "FAILED" :
                    results[i].result > 0 ? "ABORTED" :
@@ -452,12 +470,13 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
   }
   ncqprefix(nsdelta, NANOSECS_IN_SEC, timebuf, 0);
   ncbprefix(totalbytes, 1, totalbuf, 0);
-  table_segment(n, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n");
+  table_segment(n, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n",
+                "--+--------+--------+-------+---------+-------+--+--+--+-------+\n");
   ncplane_putstr(n, "            ");
-  table_printf(n, "│", "%*ss", NCPREFIXFMT(timebuf));
-  table_printf(n, "│", "%7lu", totalframes);
-  table_printf(n, "│", "%*s", NCBPREFIXFMT(totalbuf));
-  //table_printf(nc, "│", "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0);
+  table_printf(n, sep, "%*ss", NCPREFIXFMT(timebuf));
+  table_printf(n, sep, "%7lu", totalframes);
+  table_printf(n, sep, "%*s", NCBPREFIXFMT(totalbuf));
+  //table_printf(nc, sep, "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0);
   ncplane_putchar(n, '\n');
   ncplane_set_fg_rgb8(n, 0xfe, 0x20, 0x76); // PANTONE Strong Red C + 3x0x20
 #ifdef DFSG_BUILD
diff --git a/src/demo/sliders.c b/src/demo/sliders.c
index da4e03916a..58ad1b1f92 100644
--- a/src/demo/sliders.c
+++ b/src/demo/sliders.c
@@ -104,10 +104,17 @@ fill_chunk(struct ncplane* n, int idx){
   ncchannel_set_rgb8(&ur, g, b, r);
   ncchannel_set_rgb8(&ll, b, r, g);
   int ret = 0;
-  if(ncplane_gradient2x1(n, -1, -1, 0, 0, ul, ur, ll, lr) <= 0){
-    ret = -1;
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    if(ncplane_gradient2x1(n, -1, -1, 0, 0, ul, ur, ll, lr) <= 0){
+      ret = -1;
+    }
+    ret |= ncplane_double_box(n, 0, channels, maxy - 1, maxx - 1, 0);
+  }else{
+    if(ncplane_gradient(n, -1, -1, 0, 0, " ", NCSTYLE_NONE, ul, ur, ll, lr) <= 0){
+      ret = -1;
+    }
+    ret |= ncplane_ascii_box(n, 0, channels, maxy - 1, maxx - 1, 0);
   }
-  ret |= ncplane_double_box(n, 0, channels, maxy - 1, maxx - 1, 0);
   if(maxx >= 4 && maxy >= 3){
     // don't zero-index to viewer
     ret |= (ncplane_printf_yx(n, (maxy - 1) / 2, (maxx - 1) / 2, "%02d", idx + 1) < 0);
@@ -122,9 +129,15 @@ draw_bounding_box(struct ncplane* n, int yoff, int xoff, int chunky, int chunkx)
   ncchannels_set_fg_rgb8(&channels, 180, 80, 180);
   //channels_set_bg_rgb8(&channels, 0, 0, 0);
   ncplane_cursor_move_yx(n, yoff, xoff);
-  ret = ncplane_rounded_box(n, 0, channels,
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    ret = ncplane_rounded_box(n, 0, channels,
+                              CHUNKS_VERT * chunky + yoff + 1,
+                              CHUNKS_HORZ * chunkx + xoff + 1, 0);
+  }else{
+    ret = ncplane_ascii_box(n, 0, channels,
                             CHUNKS_VERT * chunky + yoff + 1,
                             CHUNKS_HORZ * chunkx + xoff + 1, 0);
+  }
   return ret;
 }
 
diff --git a/src/fetch/main.c b/src/fetch/main.c
index 5fbcc79d2c..382cf90a91 100644
--- a/src/fetch/main.c
+++ b/src/fetch/main.c
@@ -456,6 +456,7 @@ xnu_ncneofetch(fetched_info* fi){
 
 static int
 drawpalette(struct notcurses* nc){
+  int showpl = 64; // show this many per line
   int psize = notcurses_palette_size(nc);
   if(psize > 256){
     psize = 256;
@@ -465,26 +466,37 @@ drawpalette(struct notcurses* nc){
   if(dimx < 64){
     return -1;
   }
-  for(int y = 0 ; y < (psize + 63) / 64 ; ++y){
-    // we show a maximum of 64 palette entries per line
-    int toshow = psize - y * 64;
-    if(toshow > 64){
-      toshow = 64;
+  int scale = notcurses_canutf8(nc) ? 2 : 1; // use half blocks for 2*showpl
+  ncplane_cursor_move_yx(n, -1, 0);
+  for(int y = 0 ; y < (psize + (showpl * scale) - 1) / showpl / scale ; ++y){
+    // we show a maximum of showpl * scale palette entries per line
+    int toshow = psize - y * showpl;
+    if(toshow > showpl){
+      toshow = showpl;
     }
     // center based on the number being shown on this line
     if(ncplane_cursor_move_yx(n, -1, (dimx - toshow) / 2)){
       return -1;
     }
-    for(unsigned x = (dimx - 64) / 2 ; x < dimx / 2 + 32 ; ++x){
-      const int truex = x - (dimx - 64) / 2;
-      if(y * 64 + truex >= psize){
+    for(unsigned x = (dimx - showpl) / 2 ; x < dimx / 2 + 32 ; ++x){
+      const int truex = x - (dimx - showpl) / 2;
+      if(y * showpl * scale + truex >= psize){
         break;
       }
-      if(ncplane_set_bg_palindex(n, y * 64 + truex)){
+      if(ncplane_set_bg_palindex(n, y * showpl * scale + truex)){
         return -1;
       }
-      if(ncplane_putchar(n, ' ') == EOF){
-        return -1;
+      if(scale == 2){
+        if(ncplane_set_fg_palindex(n, (y * showpl * scale + truex + showpl) % psize)){
+          return -1;
+        }
+        if(ncplane_putegc(n, u8"▄", NULL) == EOF){
+          return -1;
+        }
+      }else{
+        if(ncplane_putchar(n, ' ') == EOF){
+          return -1;
+        }
       }
     }
     ncplane_set_bg_default(n);
@@ -495,6 +507,15 @@ drawpalette(struct notcurses* nc){
   return 0;
 }
 
+static int
+newline_past(struct ncplane* std, struct ncplane* p){
+  if(ncplane_putchar_yx(std, ncplane_abs_y(p) + ncplane_dim_y(p) - 1,
+                        ncplane_abs_x(p) + ncplane_dim_x(p) + 1, '\n') != 1){
+    return -1;
+  }
+  return 0;
+}
+
 static int
 infoplane_notcurses(struct notcurses* nc, const fetched_info* fi,
                     int planeheight, int nextline){
@@ -598,11 +619,7 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi,
   if(chend > parend){
     ncplane_move_rel(infop, -(chend - parend), 0);
   }
-  if(ncplane_putchar_yx(std, ncplane_abs_y(infop) + ncplane_dim_y(infop) - 1,
-                        ncplane_abs_x(infop) + ncplane_dim_x(infop) + 1,
-                        '\n') != 1){
-    return -1;
-  }
+  newline_past(std, infop);
   if(notcurses_render(nc)){
     return -1;
   }
@@ -613,7 +630,6 @@ static int
 infoplane(struct notcurses* nc, const fetched_info* fi, int nextline){
   const int planeheight = 7;
   int r = infoplane_notcurses(nc, fi, planeheight, nextline);
-  r |= notcurses_stop(nc);
   return r;
 }
 
@@ -682,6 +698,7 @@ static void*
 display_thread(void* vmarshal){
   struct marshal* m = vmarshal;
   drawpalette(m->nc);
+notcurses_render(m->nc);
   ncplane_set_bg_default(notcurses_stdplane(m->nc));
   ncplane_set_fg_default(notcurses_stdplane(m->nc));
   // we've just rendered, so any necessary scrolling has been performed. draw
@@ -712,9 +729,10 @@ display_thread(void* vmarshal){
       struct ncplane* iplane = ncvisual_blit(m->nc, ncv, &vopts);
       ncvisual_destroy(ncv);
       if(iplane){
-        int x = ncplane_x(iplane);
-        ncplane_move_yx(iplane, y, x);
-        m->nextline = ncplane_abs_y(iplane) + ncplane_dim_y(iplane);
+        ncplane_scrollup_child(notcurses_stdplane(m->nc), iplane);
+        newline_past(notcurses_stdplane(m->nc), iplane);
+        notcurses_render(m->nc);
+        m->nextline = ncplane_y(iplane) + ncplane_dim_y(iplane);
         return NULL;
       }
     }
@@ -783,7 +801,7 @@ ncneofetch(struct notcurses* nc){
     return -1;
   }
   free_fetched_info(&fi);
-  return 0;
+  return notcurses_stop(nc);
 }
 
 static void
diff --git a/src/info/main.c b/src/info/main.c
index 36fe132498..7b8fe68ec8 100644
--- a/src/info/main.c
+++ b/src/info/main.c
@@ -447,11 +447,11 @@ tinfo_debug_styles(const notcurses* nc, struct ncplane* n, const char* indent){
   tinfo_debug_cap(n, "el", get_escape(ti, ESCAPE_EL));
   finish_line(n);
   ncplane_putstr(n, indent);
-  tinfo_debug_cap(n, "utf8", ti->caps.utf8);
-  tinfo_debug_cap(n, "2x1", ti->caps.halfblocks);
-  tinfo_debug_cap(n, "2x2", ti->caps.quadrants);
-  tinfo_debug_cap(n, "3x2", ti->caps.sextants);
-  tinfo_debug_cap(n, "4x2", ti->caps.braille);
+  tinfo_debug_cap(n, "utf8", notcurses_canutf8(nc));
+  tinfo_debug_cap(n, "2x1", notcurses_canhalfblock(nc));
+  tinfo_debug_cap(n, "2x2", notcurses_canquadrant(nc));
+  tinfo_debug_cap(n, "3x2", notcurses_cansextant(nc));
+  tinfo_debug_cap(n, "4x2", notcurses_canbraille(nc));
   tinfo_debug_cap(n, "img", notcurses_canopen_images(nc));
   tinfo_debug_cap(n, "vid", notcurses_canopen_videos(nc));
   tinfo_debug_cap(n, "indn", get_escape(ti, ESCAPE_INDN));
diff --git a/src/lib/direct.c b/src/lib/direct.c
index d80882533e..0e87708c5c 100644
--- a/src/lib/direct.c
+++ b/src/lib/direct.c
@@ -405,11 +405,11 @@ int ncdirect_cursor_pop(ncdirect* n){
 }
 
 static inline int
-ncdirect_align(struct ncdirect* n, ncalign_e align, int c){
+ncdirect_align(struct ncdirect* n, ncalign_e align, unsigned c){
   if(align == NCALIGN_LEFT){
     return 0;
   }
-  int cols = ncdirect_dim_x(n);
+  unsigned cols = ncdirect_dim_x(n);
   if(c > cols){
     return 0;
   }
@@ -974,7 +974,10 @@ char* ncdirect_readline(ncdirect* n, const char* prompt){
   if(fprintf(n->ttyfp, "%s", prompt) < 0){
     return NULL;
   }
-  int dimx = ncdirect_dim_x(n);
+  unsigned dimx = ncdirect_dim_x(n);
+  if(dimx == 0){
+    return NULL;
+  }
   // FIXME what if we're reading from redirected input, not a terminal?
   unsigned y, xstart;
   if(cursor_yx_get(n, u7, &y, &xstart)){
diff --git a/src/lib/fill.c b/src/lib/fill.c
index 9cc8eeae7b..e17c828734 100644
--- a/src/lib/fill.c
+++ b/src/lib/fill.c
@@ -26,8 +26,9 @@ ncplane_polyfill_inner(ncplane* n, unsigned y, unsigned x, const nccell* c, cons
     return -1;
   }
   int ret = 0;
+  struct topolyfill* s;
   do{
-    struct topolyfill* s = stack;
+    s = stack;
     stack = stack->next;
     y = s->y;
     x = s->x;
@@ -37,34 +38,42 @@ ncplane_polyfill_inner(ncplane* n, unsigned y, unsigned x, const nccell* c, cons
     if(strcmp(glust, filltarg) == 0){
       ++ret;
       if(nccell_duplicate(n, cur, c) < 0){
-        // FIXME need free stack!
-        return -1;
+        goto err;
       }
 //fprintf(stderr, "blooming from %d/%d ret: %d\n", y, x, ret);
       if(y){
         if(create_polyfill_op(y - 1, x, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
       if(y + 1 < n->leny){
         if(create_polyfill_op(y + 1, x, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
       if(x){
         if(create_polyfill_op(y, x - 1, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
       if(x + 1 < n->lenx){
         if(create_polyfill_op(y, x + 1, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
     }
     free(s);
   }while(stack);
   return ret;
+
+err:
+  free(s);
+  while(stack){
+    struct topolyfill* tmp = stack->next;
+    free(stack);
+    stack = tmp;
+  }
+  return -1;
 }
 
 // at the initial step only, invalid ystart, xstart is an error, so explicitly check.
diff --git a/src/lib/internal.h b/src/lib/internal.h
index 0dbed48231..a794a3b6a5 100644
--- a/src/lib/internal.h
+++ b/src/lib/internal.h
@@ -1855,6 +1855,7 @@ emit_scrolls(const tinfo* ti, int count, fbuf* f){
   if(ind == NULL){
     ind = "\v";
   }
+  // fell through if we had no indn
   while(count > 0){
     if(fbuf_emit(f, ind) < 0){
       return -1;
diff --git a/src/lib/linux.c b/src/lib/linux.c
index a79e5e367d..6922c2f8f7 100644
--- a/src/lib/linux.c
+++ b/src/lib/linux.c
@@ -402,6 +402,7 @@ program_line_drawing_chars(int fd, struct unimapdesc* map){
       for(size_t widx = 0 ; widx < wcslen(s->ws) ; ++widx){
         if(!found[widx]){
           if(add_to_map(map, s->ws[widx], fontidx)){
+            free(found);
             return -1;
           }
           ++toadd;
diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c
index 56d4af895d..0d908a6c8e 100644
--- a/src/lib/notcurses.c
+++ b/src/lib/notcurses.c
@@ -1053,6 +1053,7 @@ notcurses_early_init(const struct notcurses_options* opts, FILE* fp, unsigned* u
     }
     if(opts->termtype){
       if(putenv_term(opts->termtype)){
+        free(ret);
         return NULL;
       }
     }
@@ -1673,6 +1674,7 @@ int ncplane_scrollup_child(ncplane* n, const ncplane* child){
     return 0;
   }
   int r = chend - parend; // how many rows we need scroll parent
+  ncplane_cursor_move_yx(n, ncplane_dim_y(n) - 1, 0);
   int ret = ncplane_scrollup(n, r);
   return ret;
 }
diff --git a/src/lib/progbar.c b/src/lib/progbar.c
index de273b0b12..b225d6ee96 100644
--- a/src/lib/progbar.c
+++ b/src/lib/progbar.c
@@ -23,6 +23,7 @@ ncprogbar* ncprogbar_create(ncplane* n, const ncprogbar_options* opts){
   ret->retrograde = opts->flags & NCPROGBAR_OPTION_RETROGRADE;
   if(ncplane_set_widget(n, ret, (void(*)(void*))ncprogbar_destroy)){
     ncplane_destroy(n);
+    free(ret);
     return NULL;
   }
   return ret;
diff --git a/src/lib/visual.c b/src/lib/visual.c
index 6f5cee8dff..374bac6800 100644
--- a/src/lib/visual.c
+++ b/src/lib/visual.c
@@ -1256,8 +1256,9 @@ ncvisual_polyfill_core(ncvisual* n, unsigned y, unsigned x, uint32_t rgba, uint3
   stack->x = x;
   stack->next = NULL;
   int ret = 0;
+  struct topolyfill* s;
   do{
-    struct topolyfill* s = stack;
+    s = stack;
     stack = s->next;
     y = s->y;
     x = s->x;
@@ -1268,29 +1269,37 @@ ncvisual_polyfill_core(ncvisual* n, unsigned y, unsigned x, uint32_t rgba, uint3
       *pixel = rgba;
       if(y){
         if(create_polyfill_op(y - 1, x, &stack) == NULL){
-          // FIXME need free stack!
-          return -1;
+          goto err;
         }
       }
       if(y + 1 < n->pixy){
         if(create_polyfill_op(y + 1, x, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
       if(x){
         if(create_polyfill_op(y, x - 1, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
       if(x + 1 < n->pixx){
         if(create_polyfill_op(y, x + 1, &stack) == NULL){
-          return -1;
+          goto err;
         }
       }
     }
     free(s);
   }while(stack);
   return ret;
+
+err:
+  free(s);
+  while(stack){
+    s = stack->next;
+    free(stack);
+    stack = s;
+  }
+  return -1;
 }
 
 int ncvisual_polyfill_yx(ncvisual* n, unsigned y, unsigned x, uint32_t rgba){
diff --git a/tools/nuspec b/tools/nuspec
index fe1dba32f7..8dcb4f744f 100644
--- a/tools/nuspec
+++ b/tools/nuspec
@@ -3,7 +3,7 @@
     <!-- need 4.9.0+ for SPDX license expressions -->
     <metadata minClientVersion="4.9.0">
         <id>Notcurses</id>
-        <version>2.4.99</version>
+        <version>2.4.999</version>
         <title>Notcurses TUI/CLI library</title>
         <description>Library for blingful TUIs and character graphics</description>
         <authors>nick black</authors>


Test patch:
diff --git a/doc/man/man1/notcurses-tester.1.md b/doc/man/man1/notcurses-tester.1.md
index 8d3eeb9a08..b0093ce936 100644
--- a/doc/man/man1/notcurses-tester.1.md
+++ b/doc/man/man1/notcurses-tester.1.md
@@ -1,6 +1,6 @@
 % notcurses-tester(1)
 % nick black <nickblack@linux.com>
-% v2.4.99
+% v2.4.999
 
 # NAME