Repository: dankamongmen/notcurses (language: python)

Issue:
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking
smooth bitmap movement with sixel graphics
in #1682 we introduced the pixel-offset fields to `ncvisual_options`, and added support for Kitty graphics. we ought do the same for Sixel graphics. there we have no in-protocol support; we would need add the necessary transparency rows and columns to the image ourself, with substantially increased processing requirements. this might be post-3.0 material as a result.
valgrind reports leaks when using ncvisuals
I was trying to fix memleaks in the rust bindings when passing mutable strings to C and having interesting lifetime problems, but it turns out I was also getting errors when using ncvisuals, and when trying to debug them I found out valgrind also detects errors in the C code, like the `pixel`, and `interp` *pocs*.

@dankamongmen does it happen for you too? or is my system just a bit tad strange? 

`valgrind --leak-check=full -s BIN` to replicate
valgrind reports leaks when using ncvisuals
I was trying to fix memleaks in the rust bindings when passing mutable strings to C and having interesting lifetime problems, but it turns out I was also getting errors when using ncvisuals, and when trying to debug them I found out valgrind also detects errors in the C code, like the `pixel`, and `interp` *pocs*.

@dankamongmen does it happen for you too? or is my system just a bit tad strange? 

`valgrind --leak-check=full -s BIN` to replicate
constify strings in ncselector_item
@joseluis would appreciate it if we made the `char*`s in `ncselector_item` and `ncmultiselector_item` `const`, which is a reasonable ask. we'd just need introduce internal types that don't have the `const`, and use those for our storage. easy enough.
windows ersatz problems
Some things I've noticed recently working on windows:

* [x] `uniblock` is broken with "couldn't convert 256 (100) () (Illegal byte sequence)"
* [x] `notcurses-info` prints no glyphs on a line after any failed glyph, it seems
* [x] can we detect whether we're in a working mintty? i think so -- error out on windows if we can't get ConsoleHandles (see #2248)
* [x] `normal` fails on bad rotation characters 0xe2, happens on first rotation
* [x] `animate` fails
* [x] need pipe-like input notification
* [x] `[eagle]` fails somewhere in `ncvisual_destroy()`, and it's an immediate exit, not a true error

i'd like to have `notcurses-demo` working all the way through on windows soon.
windows ersatz problems
Some things I've noticed recently working on windows:

* [x] `uniblock` is broken with "couldn't convert 256 (100) () (Illegal byte sequence)"
* [x] `notcurses-info` prints no glyphs on a line after any failed glyph, it seems
* [x] can we detect whether we're in a working mintty? i think so -- error out on windows if we can't get ConsoleHandles (see #2248)
* [x] `normal` fails on bad rotation characters 0xe2, happens on first rotation
* [x] `animate` fails
* [x] need pipe-like input notification
* [x] `[eagle]` fails somewhere in `ncvisual_destroy()`, and it's an immediate exit, not a true error

i'd like to have `notcurses-demo` working all the way through on windows soon.
misidentifying mintty as windows terminal
When running `mintty -P on` in MSYS2, we identify the terminal as `TERM_MSTERMINAL`, due to it now properly acquiring the ConsoleHandles (OH SHIT this explains how to solve #2244 subissue 3 -- just *exit* on Windows if we can't get the handles!). That's an insufficient litmus now that @mintty has shown us the power of `mintty -P on`. Continue to run detection after acquiring the handles, and prefer MinTTY on a hard match. Fall back to MSTERMINAL.
valgrind reports leaks when using ncvisuals
I was trying to fix memleaks in the rust bindings when passing mutable strings to C and having interesting lifetime problems, but it turns out I was also getting errors when using ncvisuals, and when trying to debug them I found out valgrind also detects errors in the C code, like the `pixel`, and `interp` *pocs*.

@dankamongmen does it happen for you too? or is my system just a bit tad strange? 

`valgrind --leak-check=full -s BIN` to replicate
constify items reference in ncselector_item and ncmselector_item
This is a step more on top of #2267

I made some tests and it seems to work fine without needing to pass a mutable reference
ncvisual_render needs be pile-aware
When `ncvisual_render()` is called with a `ncvisual_options` containing a `NULL` value for `n` (i.e. the create a new plane case), the `y` and `x` fields are interpreted as origins of the new plane. Said plane will always be placed relative to the standard pile. We should be able to create a visual plane in a different pile. Sure, the user can always immediately move the plane, but this means `ncvisual_render()` cannot be used without a target plane by some other pile when the standard pile is being rendered (or any number of other things).

ncvisual_render needs be pile-aware
When `ncvisual_render()` is called with a `ncvisual_options` containing a `NULL` value for `n` (i.e. the create a new plane case), the `y` and `x` fields are interpreted as origins of the new plane. Said plane will always be placed relative to the standard pile. We should be able to create a visual plane in a different pile. Sure, the user can always immediately move the plane, but this means `ncvisual_render()` cannot be used without a target plane by some other pile when the standard pile is being rendered (or any number of other things).

clear all deprecated elements before calling it abi3
There's been a good deal of deprecated material collecting, which we can't remove until ABI3, intended to be marked by 3.0.0.

I've been trying to move it to the bottom of `notcurses.h` and `ncdirect.h`, but we ought do a final pass.

All shipped code ought already have moved off of deprecated material, of course.

Also look for anything we can move to a `static inline` and away from a symbol. Symbols can't be promoted to static inline, or else old programs will stop linking.

a partial list, growing as things change:

* [x] remove `renderfile` from `notcurses_options` #2081
* [x] convert all `notcurses_can_*()` functions to `static inline` wrappers around `notcurses_capabilities()` #1768 
* [x] convert all non-`inline` `ncdirect_can_*()` functions to `static inline`s around `ncdirect_capabilities()` #1768
* [x] kill all functions marked as `((deprecated))`
* [x] kill old `CELL_ALPHA_*` definitions
* [x] kill old `palette256` type
* [x] `notcurses_render_to_buffer()`,  `notcurses_render_to_file()`
* [x] `notcurses_render()` becomes `static inline`
* [x] `nccell_width()` is removed
* [x] move fields around in `ncstats` to match documentation
* [x] remove the unused third parameter from `ncdirect_getc()` and `notcurses_getc()` #1967
* [x] renumber our synthesized `NCKEY` constants to match the kitty protocol (see #2131)
* [x] `ncstrwidth` becomes `static inline` (see #2153)
* [x] remove definitions of `notcurses_getc()` and `ncdirect_getc()`
* [x] stylemask functions ought return `uint16_t` (see #2200)
* [x] `ncplane_move_below()` and `ncplane_move_above()` ought be modified to accept `NULL`, with obvious implications, and `ncplane_move_top()` and `ncplane_move_bottom()` can be rewritten as `static inlines` around them, cutting two symbols
* [x] rename `ncplane_pixelgeom` to `ncplane_pixel_geom`
* [x] rename `ncplane_moverel` to `ncplane_move_rel`
* [x] change all geometry params and especially returns from `int` to `unsigned`
* [x] require `ncpile*` (from `ncplane*`) instead of `notcurses*` for `ncvisual_render()`, `NULL` for new pile (#1462)
* [x] `notcurses_get()` ought take an absolute deadline rather than a delay bound. kill off `delaybound_to_deadline()` once this is done (see #2302)
* [x] `ncdirect_rounded_box()` and `ncdirect_double_box()` ought become `static inline`
* [x] `notcurses_top()` and `notcurses_bottom()` can become static inlines around `ncpile_{top, bottom}()`
* [x] `ncmetric()` can become `static inline`

you know, maybe we don't want to nuke everything that's deprecated; where possible, we could keep them as (deprecated) `static inline`s, so a recompile still works. maybe.

rejected:

*~~`ncplane_erase()` becomes static inline around `ncplane_erase_region()`, which calls the current internal `ncplane_erase()` for that special case (it's a good optimization)~~ (dumb idea. this would just require traversing all the checks in `ncplane_erase_region()` for no good reason)
* ~~`notcurses_getvec()` can become static inline~~ the whole point of `notcurses_getvec()` is speed, and we can be faster if we embed this behind the locking


Discussion / hints:
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?




`c32rtomb()` works to convert most of our `unicode` glyphs. `nl_langinfo()` indicates "UTF-8".
`c32rtomb()` works to convert most of our `unicode` glyphs. `nl_langinfo()` indicates "UTF-8".
Also, we probably want to use terminfo with MinTTY, as it works within MSYS2.
see #2245


Breaking the sprixel cache up among piles isn't hard, and indeed I'm about to check that in. The hard part is ensuring, say, that a sprixel cleaned up by the destruction of a pile gets turned into an actual kill, since it won't show up in the render cycle. Probably need to keep a context-wide list of those. Figure this out for 3.0.0.
one thing i'm pretty sure we need is to move the damage detection entiretly into rasterization. right now we do it primarily in rendering, but under the multipile model, the most recent frame (against which we must perform damage detection) isn't decided until we rasterize. we'll still need to set damage in the rendering phase for e.g. sprixel invalidations, but all `cellcmp_and_dupfar()` needs to take place during rasterization for sure. this is a major outstanding bug in our model, unless i'm completely missing something.
Breaking the sprixel cache up among piles isn't hard, and indeed I'm about to check that in. The hard part is ensuring, say, that a sprixel cleaned up by the destruction of a pile gets turned into an actual kill, since it won't show up in the render cycle. Probably need to keep a context-wide list of those. Figure this out for 3.0.0.
one thing i'm pretty sure we need is to move the damage detection entiretly into rasterization. right now we do it primarily in rendering, but under the multipile model, the most recent frame (against which we must perform damage detection) isn't decided until we rasterize. we'll still need to set damage in the rendering phase for e.g. sprixel invalidations, but all `cellcmp_and_dupfar()` needs to take place during rasterization for sure. this is a major outstanding bug in our model, unless i'm completely missing something.
so ought we deprecate `notcurses_render()`?
we're removing the deprecated `notcurses_render_to_file()`, but what about `ncpile_render_to_file()`? are we keeping that?

Fix patch:
diff --git a/NEWS.md b/NEWS.md
index 41deb7bcf3..b9dadabeb1 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -10,6 +10,20 @@ rearrangements of Notcurses.
     without an obvious replacement is the `renderfp` field of
     `notcurses_options`, for which I make no apology. If you've been avoiding
     deprecated functionality, ABI3 ought require small changes, if any.
+  * The handling of geometry and distance has been normalized across all
+    functions. Lengths are now `unsigned` as opposed to `int`. Where -1 was
+    being used to indicate "everything", 0 is now required. This affects
+    `ncplane_as_rgba()`, `ncplane_contents()`, and `ncvisual_from_plane()`,
+    which all used -1. A length of zero passed to line-drawing functions is
+    now an error. Several line-drawing functions now reliably return errors
+    as opposed to short successes. Dimensions of 0 to `ncplane_mergedown()`
+    now mean "everything". Almost all coordinates now accept -1 to indicate the
+    current cursor position in that dimension. `ncplane_highgradient()` has
+    been deprecated in favor of the new `ncplane_gradient2x1()`, which takes
+    origin coordinates. `ncplane_format()` and `ncplane_stain()` now take
+    origin coordinates. All now interpret their `unsigned` argument as
+    lengths rather than closing coordinates, observing the same semantics as
+    outlined above.
 
 * 2.4.9 (not yet released)
   * Added `ncnmetric()`, which uses `snprintf()` internally. `ncmetric()`
@@ -28,20 +42,6 @@ rearrangements of Notcurses.
     `notcurses_mice_disable()` is now a `static inline` wrapper around the
     former, passing 0 as the event mask. This can be used to get mouse
     movement buttons and focus events, which were previously unavailable.
-  * The handling of geometry and distance has been normalized across all
-    functions. Lengths are now `unsigned` as opposed to `int`. Where -1 was
-    being used to indicate "everything", 0 is now required. This affects
-    `ncplane_as_rgba()`, `ncplane_contents()`, and `ncvisual_from_plane()`,
-    which all used -1. A length of zero passed to line-drawing functions is
-    now an error. Several line-drawing functions now reliably return errors
-    as opposed to short successes. Dimensions of 0 to `ncplane_mergedown()`
-    now mean "everything". Almost all coordinates now accept -1 to indicate the
-    current cursor position in that dimension. `ncplane_highgradient()` has
-    been deprecated in favor of the new `ncplane_gradient2x1()`, which takes
-    origin coordinates. `ncplane_format()` and `ncplane_stain()` now take
-    origin coordinates. All now interpret their `unsigned` argument as
-    lengths rather than closing coordinates, observing the same semantics as
-    outlined above.
   * `ncvisual_geom()` has been introduced, using the `ncvgeom` struct
     introduced for direct mode. This allows complete statement of geometry
     for an `ncvisual`. It replaces `ncvisual_blitter_geom()`, which has been
@@ -231,7 +231,7 @@ rearrangements of Notcurses.
   * `notcurses_detected_terminal()` and `ncdirect_detected_terminal()` now
     both return a heap-allocated string, which will contain the terminal
     version if Notcurses was able to detect it. This result ought be free()d.
-  * Added `ncplane_moverel()`.
+  * Added `ncplane_move_rel()`.
   * Documented `ncplane_move_yx()` in `notcurses_plane.3`, and removed the
     false comment that "passing -1 as a coordinate will hold that axis
     constant" from `USAGE.md` and `notcurses.h`. This has never been true.
diff --git a/USAGE.md b/USAGE.md
index 681883a446..d1c6880f58 100644
--- a/USAGE.md
+++ b/USAGE.md
@@ -223,8 +223,10 @@ int notcurses_render(struct notcurses* nc);
 // must be freed by the caller.
 int ncpile_render_to_buffer(struct ncplane* p, char** buf, size_t* buflen);
 
-// Write the last rendered frame, in its entirety, to 'fp'. If
-// notcurses_render() has not yet been called, nothing will be written.
+// Write the last rendered frame, in its entirety, to 'fp'. If a frame has
+// not yet been rendered, nothing will be written.
+int ncpile_render_to_file(struct ncplane* p, FILE* fp);
+
 int ncpile_render_to_file(struct ncplane* p, FILE* fp);
 
 // Retrieve the contents of the specified cell as last rendered. The EGC is
@@ -1045,7 +1047,7 @@ int ncplane_move_yx(struct ncplane* n, int y, int x);
 // Move this plane relative to its current location. Negative values move up
 // and left, respectively. Pass 0 to hold an axis constant.
 __attribute__ ((nonnull (1))) static inline int
-ncplane_moverel(struct ncplane* n, int y, int x){
+ncplane_move_rel(struct ncplane* n, int y, int x){
   int oy, ox;
   ncplane_yx(n, &oy, &ox);
   return ncplane_move_yx(n, oy + y, ox + x);
@@ -1084,10 +1086,10 @@ ncplane_dim_x(const struct ncplane* n){
 // ('celldimy', 'celldimx'), and the maximum displayable bitmap ('maxbmapy',
 // 'maxbmapx'). If bitmaps are not supported, 'maxbmapy' and 'maxbmapx' will
 // be 0. Any of the geometry arguments may be NULL.
-void ncplane_pixelgeom(struct ncplane* n,
-                       unsigned* restrict pxy, unsigned* restrict pxx,
-                       unsigned* restrict celldimy, unsigned* restrict celldimx,
-                       unsigned* restrict maxbmapy, unsigned* restrict maxbmapx);
+void ncplane_pixel_geom(struct ncplane* n,
+                        unsigned* restrict pxy, unsigned* restrict pxx,
+                        unsigned* restrict celldimy, unsigned* restrict celldimx,
+                        unsigned* restrict maxbmapy, unsigned* restrict maxbmapx);
 
 // provided a coordinate relative to the origin of 'src', map it to the same
 // absolute coordinate relative to the origin of 'dst'. either or both of 'y'
@@ -1144,8 +1146,17 @@ int ncplane_base(struct ncplane* ncp, nccell* c);
 
 ```c
 // Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or bottom.
-void ncplane_move_top(struct ncplane* n);
-void ncplane_move_bottom(struct ncplane* n);
+__attribute__ ((nonnull (1)))
+static inline void
+ncplane_move_top(struct ncplane* n){
+  ncplane_move_below(n, NULL);
+}
+
+__attribute__ ((nonnull (1)))
+static inline void
+ncplane_move_bottom(struct ncplane* n){
+  ncplane_move_above(n, NULL);
+}
 
 // Splice ncplane 'n' and its bound planes out of the z-buffer, and reinsert
 // them at the top or bottom. Relative order will be maintained between the
@@ -3602,12 +3613,12 @@ typedef struct ncstats {
   uint64_t writeouts;        // successful ncpile_rasterize() runs
   uint64_t failed_renders;   // aborted renders, should be 0
   uint64_t failed_writeouts; // aborted writes
-  uint64_t render_bytes;     // bytes emitted to ttyfp
-  int64_t render_max_bytes;  // max bytes emitted for a frame
-  int64_t render_min_bytes;  // min bytes emitted for a frame
+  uint64_t raster_bytes;     // bytes emitted to ttyfp
+  int64_t raster_max_bytes;  // max bytes emitted for a frame
+  int64_t raster_min_bytes;  // min bytes emitted for a frame
   uint64_t render_ns;        // nanoseconds spent rendering
-  int64_t render_max_ns;     // max ns spent in render+raster for a frame
-  int64_t render_min_ns;     // min ns spent in render+raster for a frame
+  int64_t render_max_ns;     // max ns spent in render for a frame
+  int64_t render_min_ns;     // min ns spent in render for a frame
   uint64_t raster_ns;        // nanoseconds spent rasterizing
   int64_t raster_max_ns;     // max ns spent in raster for a frame
   int64_t raster_min_ns;     // min ns spent in raster for a frame
@@ -3626,8 +3637,9 @@ typedef struct ncstats {
   uint64_t sprixelemissions; // sprixel draw count
   uint64_t sprixelelisions;  // sprixel elision count
   uint64_t sprixelbytes;     // sprixel bytes emitted
-  uint64_t appsync_updates;  // application-synchronized updates
+  uint64_t appsync_updates;  // how many application-synchronized updates?
   uint64_t input_errors;     // errors processing control sequences/utf8
+  uint64_t input_events;     // characters returned to userspace
   uint64_t hpa_gratuitous;   // unnecessary hpas issued
 
   // current state -- these can decrease
diff --git a/doc/man/man3/notcurses_input.3.md b/doc/man/man3/notcurses_input.3.md
index ec8111432d..425a7b3ccd 100644
--- a/doc/man/man3/notcurses_input.3.md
+++ b/doc/man/man3/notcurses_input.3.md
@@ -197,7 +197,7 @@ issues are resolved. You can determine whether the protocol is in use
 by examining the output of **notcurses-info(1)**. If the **kbd** property
 is indicated, you're using the Kitty protocol.
 
-Mouse events in the top and left margins will never be delivered to the
+Mouse events in the left margins will never be delivered to the
 application (as is intended), but mouse events in the bottom and right margins
 sometimes can be if the event occurs prior to a window resize.
 
diff --git a/doc/man/man3/notcurses_metric.3.md b/doc/man/man3/notcurses_metric.3.md
index c6b41aec8b..c83c807a1c 100644
--- a/doc/man/man3/notcurses_metric.3.md
+++ b/doc/man/man3/notcurses_metric.3.md
@@ -23,7 +23,7 @@ notcurses_metric - fixed-width numeric output with metric suffixes
 #define NCBPREFIXFMT(x) NCMETRICFWIDTH((x), NCBPREFIXCOLUMNS), (x)
 ```
 
-**const char* ncmetric(uintmax_t ***val***, uintmax_t ***decimal***, char* ***buf***, int ***omitdec***, unsigned ***mult***, int ***uprefix***);**
+**const char* ncnmetric(uintmax_t ***val***, size_t s, uintmax_t ***decimal***, char* ***buf***, int ***omitdec***, unsigned ***mult***, int ***uprefix***);**
 
 **static inline const char* ncqprefix(uintmax_t ***val***, uintmax_t ***decimal***, char* ***buf***, int ***omitdec***);**
 
@@ -44,15 +44,18 @@ Giga, Tera, Peta, Exa, Zetta, and Yotta) and the small suffixes mµnpfazy
 range 1e24 (one septillion) through 1e-24, sufficing for all possible values of
 a 64-bit **uintmax_t**.
 
-**val** is the value being output, having been scaled by **decimal**.
-**decimal** will typically be 1; to represent values less than 1, **decimal**
-should be larger than **val**. The output will be written to **buf**, which
+***val*** is the value being output, having been scaled by ***decimal***.
+***decimal*** will typically be 1; to represent values less than 1, ***decimal***
+should be larger than ***val***. The output will be written to ***buf***, which
 must be at least:
 
 * **NCPREFIXSTRLEN** + 1 bytes for a 1000-based value
 * **NCIPREFIXSTRLEN** + 1 bytes for a 1024-based value
 * **NCBPREFIXSTRLEN** + 1 bytes for a 1024-based value with an 'i' suffix
 
+***s*** is the maximum output size, including '\0', used in the same
+fashion as **snprintf(3)** (which **ncnmetric** calls).
+
 Three helper functions are provided to simplify these common cases:
 
 ```
@@ -78,7 +81,7 @@ ncbprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
 If **omitdec** is not zero, the decimal point and mantissa will be
 omitted if all digits to be displayed would be zero. The decimal point takes
 the current locale into account (see **setlocale(3)** and **localeconv(3)**).
-**mult** is the relative multiple for each suffix. **uprefix**, if not zero,
+***mult*** is the relative multiple for each suffix. ***uprefix***, if not zero,
 will be used as a suffix following any metric suffix.
 
 The maximum number of columns is not directly related to the maximum number of
@@ -93,9 +96,9 @@ length are:
 In general, the maximum-width output will take the form **CCC.mmMu**, where C
 are digits of the characteristic (up to ceil(log10(**mult**)) digits), the
 decimal point follows, m are digits of the mantissa (up to 2), M is the metric
-suffix, and u is the **uprefix**. The minimum-width output will take the form
-**C**. This minimal form can occur if **omitdec** is non-zero and a
-single-column value such as 5 is passed for **val**.
+suffix, and u is the ***uprefix***. The minimum-width output will take the form
+**C**. This minimal form can occur if ***omitdec*** is non-zero and a
+single-column value such as 5 is passed for ***val***.
 
 Three more defines are provided to simplify formatted fixed-width output using
 the results of **ncmetric**. Each of these macros accepts a character buffer
@@ -113,26 +116,26 @@ to ensure that the output is always **NCPREFIXCOLUMNS** wide.
 
 # RETURN VALUES
 
-**NULL** if input parameters were invalid. Otherwise, a pointer to **buf**,
+**NULL** if input parameters were invalid. Otherwise, a pointer to ***buf***,
 filled in with the formatted output.
 
 # EXAMPLES
 
-**ncmetric(0, 1, buf, 0, 1000, '\0')**: "0.00".
+**ncnmetric(0, INT_MAX, buf, 0, 1000, '\0')**: "0.00".
 
-**ncmetric(0, 1, buf, 1, 1000, '\0')**: "0".
+**ncnmetric(0, INT_MAX, buf, 1, 1000, '\0')**: "0".
 
-**ncmetric(1023, 1, buf, 1, 1000, '\0')**: "1.02K".
+**ncnmetric(1023, INT_MAX, buf, 1, 1000, '\0')**: "1.02K".
 
-**ncmetric(1023, 1, buf, 1, 1024, 'i')**: "1023".
+**ncnmetric(1023, INT_MAX, buf, 1, 1024, 'i')**: "1023".
 
-**ncmetric(1024, 1, buf, 1, 1024, 'i')**: "1Ki".
+**ncnmetric(1024, INT_MAX, buf, 1, 1024, 'i')**: "1Ki".
 
-**ncmetric(4096, 1, buf, 0, 1024, 'i')**: "4.00Ki".
+**ncnmetric(4096, INT_MAX, buf, 0, 1024, 'i')**: "4.00Ki".
 
-**ncmetric(0x7fffffffffffffff, 1, buf, 0, 1000, '\0')**: "9.22E".
+**ncnmetric(0x7fffffffffffffff, INT_MAX, buf, 0, 1000, '\0')**: "9.22E".
 
-**ncmetric(0xffffffffffffffff, 1, buf, 0, 1000, '\0')**: "18.45E".
+**ncnmetric(0xffffffffffffffff, INT_MAX, buf, 0, 1000, '\0')**: "18.45E".
 
 # BUGS
 
@@ -143,4 +146,5 @@ This function is difficult to understand.
 **localeconv(3)**,
 **notcurses(3)**,
 **notcurses_output(3)**,
-**setlocale(3)**
+**setlocale(3)**,
+**snprintf(3)**
diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md
index 2c403092f3..e03d48604f 100644
--- a/doc/man/man3/notcurses_plane.3.md
+++ b/doc/man/man3/notcurses_plane.3.md
@@ -71,7 +71,7 @@ typedef struct ncplane_options {
 
 **int ncplane_move_yx(struct ncplane* ***n***, int ***y***, int ***x***);**
 
-**int ncplane_moverel(struct ncplane* ***n***, int ***y***, int ***x***);**
+**int ncplane_move_rel(struct ncplane* ***n***, int ***y***, int ***x***);**
 
 **void ncplane_yx(const struct ncplane* ***n***, int* restrict ***y***, int* restrict ***x***);**
 
@@ -95,9 +95,9 @@ typedef struct ncplane_options {
 
 **int ncplane_base(struct ncplane* ***ncp***, nccell* ***c***);**
 
-**void ncplane_move_top(struct ncplane* ***n***);**
+**static inline void ncplane_move_top(struct ncplane* ***n***);**
 
-**void ncplane_move_bottom(struct ncplane* ***n***);**
+**static inline void ncplane_move_bottom(struct ncplane* ***n***);**
 
 **void ncplane_move_family_top(struct ncplane* ***n***);**
 
@@ -225,7 +225,7 @@ typedef struct ncplane_options {
 
 **int ncplane_rotate_ccw(struct ncplane* ***n***);**
 
-**void ncplane_pixelgeom(const struct notcurses* ***n***, unsigned* restrict ***pxy***, unsigned* restrict ***pxx***, unsigned* restrict ***celldimy***, unsigned* restrict ***celldimx***, unsigned* restrict ***maxbmapy***, unsigned* restrict ***maxbmapx***);**
+**void ncplane_pixel_geom(const struct notcurses* ***n***, unsigned* restrict ***pxy***, unsigned* restrict ***pxx***, unsigned* restrict ***celldimy***, unsigned* restrict ***celldimx***, unsigned* restrict ***maxbmapy***, unsigned* restrict ***maxbmapx***);**
 
 **int ncplane_set_name(struct ncplane* ***n***, const char* ***name***);**
 
@@ -433,7 +433,7 @@ they intersect the plane. This can be disabled with the
 
 ## Bitmaps
 
-**ncplane_pixelgeom** retrieves pixel geometry details. **pxy** and **pxx**
+**ncplane_pixel_geom** retrieves pixel geometry details. **pxy** and **pxx**
 return the size of the plane in pixels. **celldimy** and **celldimx** return
 the size of a cell in pixels (these ought be the same across planes).
 **maxbmapy** and **maxbmapx** describe the largest bitmap which can be
@@ -505,9 +505,6 @@ All other functions cannot fail (and return **void**).
 
 # NOTES
 
-**ncplane_new** is defined as a deprecated wrapper around **ncplane_create**.
-It should not be used in new code.
-
 # BUGS
 
 **ncplane_at_yx** doesn't yet account for bitmap-based graphics (see
diff --git a/doc/man/man3/notcurses_render.3.md b/doc/man/man3/notcurses_render.3.md
index a2f85d2621..1e317e7be6 100644
--- a/doc/man/man3/notcurses_render.3.md
+++ b/doc/man/man3/notcurses_render.3.md
@@ -63,9 +63,7 @@ requires determining an extended grapheme cluster, foreground color, background
 color, and style for each cell of the physical terminal. Writing the scene
 requires synthesizing a set of UTF-8-encoded characters and escape codes
 appropriate for the terminal (relying on terminfo(5)), and writing this
-sequence to the output **FILE**. If the **renderfp** value was not NULL in the
-original call to **notcurses_init**, the frame will be written to that **FILE**
-as well. This write does not affect statistics.
+sequence to the output **FILE**.
 
 Each cell can be rendered in isolation, though synthesis of the stream carries
 dependencies between cells.
diff --git a/doc/man/man3/notcurses_stats.3.md b/doc/man/man3/notcurses_stats.3.md
index e2b78bb024..3593e06538 100644
--- a/doc/man/man3/notcurses_stats.3.md
+++ b/doc/man/man3/notcurses_stats.3.md
@@ -17,9 +17,9 @@ typedef struct ncstats {
   uint64_t writeouts;        // successful ncpile_rasterize() runs
   uint64_t failed_renders;   // aborted renders, should be 0
   uint64_t failed_writeouts; // aborted writes
-  uint64_t render_bytes;     // bytes emitted to ttyfp
-  int64_t render_max_bytes;  // max bytes emitted for a frame
-  int64_t render_min_bytes;  // min bytes emitted for a frame
+  uint64_t raster_bytes;     // bytes emitted to ttyfp
+  int64_t raster_max_bytes;  // max bytes emitted for a frame
+  int64_t raster_min_bytes;  // min bytes emitted for a frame
   uint64_t render_ns;        // nanoseconds spent rendering
   int64_t render_max_ns;     // max ns spent for a frame
   int64_t render_min_ns;     // min ns spent for a frame
@@ -76,7 +76,7 @@ renders are not expected to fail except under exceptional circumstances.
 should **notcurses_render(3)** fail while writing out a frame to the terminal,
 it counts as a failed render.
 
-**render_max_bytes** and **render_min_bytes** track the maximum and minimum
+**raster_max_bytes** and **raster_min_bytes** track the maximum and minimum
 number of bytes used rasterizing a frame. A given state of Notcurses does not
 correspond to a unique number of bytes; the size is also dependent on the
 existing terminal state. As a first approximation, the time a terminal takes to
@@ -135,7 +135,7 @@ Linux framebuffer bitmaps are not written through the terminal device, but
 instead directly into the memory-mapped framebuffer (see **mmap(2)**). Bytes
 used for framebuffer graphics are thus independent of bytes written to the
 terminal. This explains why **sprixelbytes** may be surprising given the
-value of **render_bytes**.
+value of **raster_bytes**.
 
 # RETURN VALUES
 
diff --git a/include/ncpp/NotCurses.hh b/include/ncpp/NotCurses.hh
index 416ad28483..2a8d1308fa 100644
--- a/include/ncpp/NotCurses.hh
+++ b/include/ncpp/NotCurses.hh
@@ -66,7 +66,7 @@ namespace ncpp
 
 		static const char* ncmetric (uintmax_t val, uintmax_t decimal, char *buf, int omitdec, unsigned mult, int uprefix) noexcept
 		{
-			return ::ncmetric (val, decimal, buf, omitdec, mult, uprefix);
+			return ::ncnmetric (val, INT_MAX, decimal, buf, omitdec, mult, uprefix);
 		}
 
 		static const char* ncqprefix (uintmax_t val, uintmax_t decimal, char *buf, int omitdec) noexcept
diff --git a/include/notcurses/direct.h b/include/notcurses/direct.h
index 063a88e20c..35118b77c8 100644
--- a/include/notcurses/direct.h
+++ b/include/notcurses/direct.h
@@ -71,43 +71,11 @@ API int ncdirect_set_fg_rgb(struct ncdirect* nc, unsigned rgb)
 API int ncdirect_set_bg_rgb(struct ncdirect* nc, unsigned rgb)
   __attribute__ ((nonnull (1)));
 
-static inline int ncdirect_fg_rgb(struct ncdirect* nc, unsigned rgb)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_fg_rgb(struct ncdirect* nc, unsigned rgb){
-  return ncdirect_set_fg_rgb(nc, rgb);
-}
-
-static inline int ncdirect_bg_rgb(struct ncdirect* nc, unsigned rgb)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_bg_rgb(struct ncdirect* nc, unsigned rgb){
-  return ncdirect_set_bg_rgb(nc, rgb);
-}
-
 API int ncdirect_set_fg_palindex(struct ncdirect* nc, int pidx)
   __attribute__ ((nonnull (1)));
 API int ncdirect_set_bg_palindex(struct ncdirect* nc, int pidx)
   __attribute__ ((nonnull (1)));
 
-static inline int ncdirect_fg_palindex(struct ncdirect* nc, int pidx)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_fg_palindex(struct ncdirect* nc, int pidx){
-  return ncdirect_set_fg_palindex(nc, pidx);
-}
-
-static inline int ncdirect_bg_palindex(struct ncdirect* nc, int pidx)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_bg_palindex(struct ncdirect* nc, int pidx){
-  return ncdirect_set_bg_palindex(nc, pidx);
-}
-
 // Returns the number of simultaneous colors claimed to be supported, or 1 if
 // there is no color support. Note that several terminal emulators advertise
 // more colors than they actually support, downsampling internally.
@@ -154,45 +122,11 @@ ncdirect_set_fg_rgb8(struct ncdirect* nc, unsigned r, unsigned g, unsigned b){
   return ncdirect_set_fg_rgb(nc, (r << 16u) + (g << 8u) + b);
 }
 
-static inline int
-ncdirect_fg_rgb8(struct ncdirect* nc, unsigned r, unsigned g, unsigned b)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_fg_rgb8(struct ncdirect* nc, unsigned r, unsigned g, unsigned b){
-  return ncdirect_set_fg_rgb8(nc, r, g, b);
-}
-
-static inline int
-ncdirect_bg_rgb8(struct ncdirect* nc, unsigned r, unsigned g, unsigned b)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_bg_rgb8(struct ncdirect* nc, unsigned r, unsigned g, unsigned b){
-  return ncdirect_set_bg_rgb8(nc, r, g, b);
-}
-
 API int ncdirect_set_fg_default(struct ncdirect* nc)
   __attribute__ ((nonnull (1)));
 API int ncdirect_set_bg_default(struct ncdirect* nc)
   __attribute__ ((nonnull (1)));
 
-static inline int ncdirect_fg_default(struct ncdirect* nc)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_fg_default(struct ncdirect* nc){
-  return ncdirect_set_fg_default(nc);
-}
-
-static inline int ncdirect_bg_default(struct ncdirect* nc)
-  __attribute__ ((deprecated));
-
-static inline int
-ncdirect_bg_default(struct ncdirect* nc){
-  return ncdirect_set_bg_default(nc);
-}
-
 // Get the current number of columns/rows.
 API unsigned ncdirect_dim_x(struct ncdirect* nc) __attribute__ ((nonnull (1)));
 API unsigned ncdirect_dim_y(struct ncdirect* nc) __attribute__ ((nonnull (1)));
@@ -201,7 +135,7 @@ API unsigned ncdirect_dim_y(struct ncdirect* nc) __attribute__ ((nonnull (1)));
 // (NCSTYLE_UNDERLINE, NCSTYLE_BOLD, etc.) The attribute is only
 // indicated as supported if the terminal can support it together with color.
 // For more information, see the "ncv" capability in terminfo(5).
-API unsigned ncdirect_supported_styles(const struct ncdirect* nc)
+API uint16_t ncdirect_supported_styles(const struct ncdirect* nc)
   __attribute__ ((nonnull (1)));
 
 // ncplane_styles_*() analogues
@@ -211,17 +145,9 @@ API int ncdirect_on_styles(struct ncdirect* n, unsigned stylebits)
   __attribute__ ((nonnull (1)));
 API int ncdirect_off_styles(struct ncdirect* n, unsigned stylebits)
   __attribute__ ((nonnull (1)));
-API unsigned ncdirect_styles(const struct ncdirect* n)
+API uint16_t ncdirect_styles(const struct ncdirect* n)
   __attribute__ ((nonnull (1)));
 
-// Deprecated forms of above.
-API int ncdirect_styles_set(struct ncdirect* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-API int ncdirect_styles_on(struct ncdirect* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-API int ncdirect_styles_off(struct ncdirect* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-
 // Move the cursor in direct mode. -1 to retain current location on that axis.
 API int ncdirect_cursor_move_yx(struct ncdirect* n, int y, int x)
   __attribute__ ((nonnull (1)));
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index cb9ce6f78c..6fccc7f546 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -95,11 +95,6 @@ typedef enum {
   NCSCALE_SCALE_HIRES,
 } ncscale_e;
 
-// Returns the number of columns occupied by a multibyte (UTF-8) string, or
-// -1 if a non-printable/illegal character is encountered.
-// FIXME becomes a static inline in ABI3.
-API int ncstrwidth(const char* mbs);
-
 // Returns the number of columns occupied by a the valid prefix of a multibyte
 // (UTF-8) string. If an invalid character is encountered, -1 will be returned,
 // and the number of valid bytes and columns will be written into *|validbytes|
@@ -107,6 +102,13 @@ API int ncstrwidth(const char* mbs);
 // *|validbytes| and *|validwidth| reflect the entire string.
 API int ncstrwidth_valid(const char* egcs, int* validbytes, int* validwidth);
 
+// Returns the number of columns occupied by a multibyte (UTF-8) string, or
+// -1 if a non-printable/illegal character is encountered.
+static inline int
+ncstrwidth(const char* mbs){
+  return ncstrwidth_valid(mbs, NULL, NULL);
+}
+
 // Returns a heap-allocated copy of the user name under which we are running.
 API ALLOC char* notcurses_accountname(void);
 
@@ -716,7 +718,7 @@ nccell_set_styles(nccell* c, unsigned stylebits){
 }
 
 // Extract the style bits from the nccell.
-static inline unsigned
+static inline uint16_t
 nccell_styles(const nccell* c){
   return c->stylemask;
 }
@@ -923,7 +925,6 @@ typedef struct notcurses_options {
   // the environment variable TERM is used. Failure to open the terminal
   // definition will result in failure to initialize notcurses.
   const char* termtype;
-  FILE* renderfp; // deprecated, must be NULL, will be removed for ABI3 FIXME
   // Progressively higher log levels result in more logging to stderr. By
   // default, nothing is printed to stderr once fullscreen service begins.
   ncloglevel_e loglevel;
@@ -981,6 +982,12 @@ API int notcurses_enter_alternate_screen(struct notcurses* nc)
 API int notcurses_leave_alternate_screen(struct notcurses* nc)
   __attribute__ ((nonnull (1)));
 
+// Get a reference to the standard plane (one matching our current idea of the
+// terminal size) for this terminal. The standard plane always exists, and its
+// origin is always at the uppermost, leftmost cell of the terminal.
+API struct ncplane* notcurses_stdplane(struct notcurses* nc);
+API const struct ncplane* notcurses_stdplane_const(const struct notcurses* nc);
+
 // Return the topmost plane of the pile containing 'n'.
 API struct ncplane* ncpile_top(struct ncplane* n)
   __attribute__ ((nonnull (1)));
@@ -1001,8 +1008,14 @@ API int ncpile_rasterize(struct ncplane* n)
   __attribute__ ((nonnull (1)));
 
 // Renders and rasterizes the standard pile in one shot. Blocking call.
-API int notcurses_render(struct notcurses* nc)
-  __attribute__ ((nonnull (1)));
+static inline int
+notcurses_render(struct notcurses* nc){
+  struct ncplane* stdn = notcurses_stdplane(nc);
+  if(ncpile_render(stdn)){
+    return -1;
+  }
+  return ncpile_rasterize(stdn);
+}
 
 // Perform the rendering and rasterization portion of ncpile_render() and
 // ncpile_rasterize(), but do not write the resulting buffer out to the
@@ -1011,8 +1024,8 @@ API int notcurses_render(struct notcurses* nc)
 API int ncpile_render_to_buffer(struct ncplane* p, char** buf, size_t* buflen)
   __attribute__ ((nonnull (1, 2, 3)));
 
-// Write the last rendered frame, in its entirety, to 'fp'. If
-// notcurses_render() has not yet been called, nothing will be written.
+// Write the last rendered frame, in its entirety, to 'fp'. If a frame has
+// not yet been rendered, nothing will be written.
 API int ncpile_render_to_file(struct ncplane* p, FILE* fp)
   __attribute__ ((nonnull (1, 2)));
 
@@ -1223,7 +1236,7 @@ ncplane_dim_x(const struct ncplane* n){
 // 'maxbmapx'). If bitmaps are not supported, or if there is no artificial
 // limit on bitmap size, 'maxbmapy' and 'maxbmapx' will be 0. Any of the
 // geometry arguments may be NULL.
-API void ncplane_pixelgeom(const struct ncplane* n,
+API void ncplane_pixel_geom(const struct ncplane* n,
                            unsigned* RESTRICT pxy, unsigned* RESTRICT pxx,
                            unsigned* RESTRICT celldimy, unsigned* RESTRICT celldimx,
                            unsigned* RESTRICT maxbmapy, unsigned* RESTRICT maxbmapx)
@@ -1420,7 +1433,7 @@ typedef struct nccapabilities {
 // (NCSTYLE_UNDERLINE, NCSTYLE_BOLD, etc.) The attribute is only
 // indicated as supported if the terminal can support it together with color.
 // For more information, see the "ncv" capability in terminfo(5).
-API unsigned notcurses_supported_styles(const struct notcurses* nc)
+API uint16_t notcurses_supported_styles(const struct notcurses* nc)
   __attribute__ ((nonnull (1))) __attribute__ ((pure));
 
 // Returns the number of simultaneous colors claimed to be supported, or 1 if
@@ -1529,13 +1542,15 @@ typedef struct ncstats {
   uint64_t writeouts;        // successful ncpile_rasterize() runs
   uint64_t failed_renders;   // aborted renders, should be 0
   uint64_t failed_writeouts; // aborted writes
-  // FIXME these next three all ought be "writeout" or "raster"
-  uint64_t render_bytes;     // bytes emitted to ttyfp
-  int64_t render_max_bytes;  // max bytes emitted for a frame
-  int64_t render_min_bytes;  // min bytes emitted for a frame
+  uint64_t raster_bytes;     // bytes emitted to ttyfp
+  int64_t raster_max_bytes;  // max bytes emitted for a frame
+  int64_t raster_min_bytes;  // min bytes emitted for a frame
   uint64_t render_ns;        // nanoseconds spent rendering
   int64_t render_max_ns;     // max ns spent in render for a frame
   int64_t render_min_ns;     // min ns spent in render for a frame
+  uint64_t raster_ns;        // nanoseconds spent rasterizing
+  int64_t raster_max_ns;     // max ns spent in raster for a frame
+  int64_t raster_min_ns;     // min ns spent in raster for a frame
   uint64_t writeout_ns;      // nanoseconds spent writing frames to terminal
   int64_t writeout_max_ns;   // max ns spent writing out a frame
   int64_t writeout_min_ns;   // min ns spent writing out a frame
@@ -1548,22 +1563,17 @@ typedef struct ncstats {
   uint64_t defaultelisions;  // default color was emitted
   uint64_t defaultemissions; // default color was elided
   uint64_t refreshes;        // refresh requests (non-optimized redraw)
-  uint64_t appsync_updates;  // how many application-synchronized updates?
-
-  // current state -- these can decrease
-  uint64_t fbbytes;          // total bytes devoted to all active framebuffers
-  unsigned planes;           // number of planes currently in existence
-
-  // FIXME placed here for ABI compatibility; move up for ABI3
-  uint64_t raster_ns;        // nanoseconds spent rasterizing
-  int64_t raster_max_ns;     // max ns spent in raster for a frame
-  int64_t raster_min_ns;     // min ns spent in raster for a frame
   uint64_t sprixelemissions; // sprixel draw count
   uint64_t sprixelelisions;  // sprixel elision count
   uint64_t sprixelbytes;     // sprixel bytes emitted
+  uint64_t appsync_updates;  // how many application-synchronized updates?
   uint64_t input_errors;     // errors processing control sequences/utf8
   uint64_t input_events;     // characters returned to userspace
   uint64_t hpa_gratuitous;   // unnecessary hpas issued
+
+  // current state -- these can decrease
+  uint64_t fbbytes;          // total bytes devoted to all active framebuffers
+  unsigned planes;           // number of planes currently in existence
 } ncstats;
 
 // Allocate an ncstats object. Use this rather than allocating your own, since
@@ -1648,7 +1658,7 @@ API int ncplane_move_yx(struct ncplane* n, int y, int x);
 // Move this plane relative to its current location. Negative values move up
 // and left, respectively. Pass 0 to hold an axis constant.
 __attribute__ ((nonnull (1))) static inline int
-ncplane_moverel(struct ncplane* n, int y, int x){
+ncplane_move_rel(struct ncplane* n, int y, int x){
   int oy, ox;
   ncplane_yx(n, &oy, &ox);
   return ncplane_move_yx(n, oy + y, ox + x);
@@ -1694,13 +1704,18 @@ API int ncplane_move_below(struct ncplane* RESTRICT n,
                            struct ncplane* RESTRICT below)
   __attribute__ ((nonnull (1)));
 
-// Splice ncplane 'n' out of the z-buffer, and reinsert it at the top or
-// bottom. FIXME these both become static inline wrappers around
-// ncplane_move_below() and ncplane_move_above() in ABI3.
-API void ncplane_move_top(struct ncplane* n)
-  __attribute__ ((nonnull (1)));
-API void ncplane_move_bottom(struct ncplane* n)
-  __attribute__ ((nonnull (1)));
+// Splice ncplane 'n' out of the z-buffer; reinsert it at the top or bottom.
+__attribute__ ((nonnull (1)))
+static inline void
+ncplane_move_top(struct ncplane* n){
+  ncplane_move_below(n, NULL);
+}
+
+__attribute__ ((nonnull (1)))
+static inline void
+ncplane_move_bottom(struct ncplane* n){
+  ncplane_move_above(n, NULL);
+}
 
 // Splice ncplane 'n' and its bound planes out of the z-buffer, and reinsert
 // them above or below 'targ'. Relative order will be maintained between the
@@ -1980,19 +1995,68 @@ API int ncplane_putwegc_stained(struct ncplane* n, const wchar_t* gclust, int* s
 // (though not beyond the end of the plane); this number is returned on success.
 // On error, a non-positive number is returned, indicating the number of columns
 // which were written before the error.
-API int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters);
+static inline int
+ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){
+  int ret = 0;
+  while(*gclusters){
+    int wcs;
+    int cols = ncplane_putegc_yx(n, y, x, gclusters, &wcs);
+//fprintf(stderr, "wrote %.*s %d cols %d bytes now at %d/%d\n", wcs, gclusters, cols, wcs, n->y, n->x);
+    if(cols < 0){
+      return -ret;
+    }
+    if(wcs == 0){
+      break;
+    }
+    // after the first iteration, just let the cursor code control where we
+    // print, so that scrolling is taken into account
+    y = -1;
+    x = -1;
+    gclusters += wcs;
+    ret += cols;
+  }
+  return ret;
+}
 
 static inline int
 ncplane_putstr(struct ncplane* n, const char* gclustarr){
   return ncplane_putstr_yx(n, -1, -1, gclustarr);
 }
 
-API int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align,
-                               const char* s);
+static inline int
+ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align, const char* s){
+  int validbytes, validwidth;
+  // we'll want to do the partial write if there's an error somewhere within
+  ncstrwidth_valid(s, &validbytes, &validwidth);
+  int xpos = ncplane_halign(n, align, validwidth);
+  if(xpos < 0){
+    return -1;
+  }
+  return ncplane_putstr_yx(n, y, xpos, s);
+}
 
 // Replace a string's worth of glyphs at the current cursor location, but
 // retain the styling. The current styling of the plane will not be changed.
-API int ncplane_putstr_stained(struct ncplane* n, const char* s);
+static inline int
+ncplane_putstr_stained(struct ncplane* n, const char* gclusters){
+  int ret = 0;
+  while(*gclusters){
+    int wcs;
+    int cols = ncplane_putegc_stained(n, gclusters, &wcs);
+    if(cols < 0){
+      return -ret;
+    }
+    if(wcs == 0){
+      break;
+    }
+    gclusters += wcs;
+    ret += cols;
+  }
+  return ret;
+}
+
+API int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* str)
+  __attribute__ ((nonnull (1, 5)));
 
 // Write a series of EGCs to the current location, using the current style.
 // They will be interpreted as a series of columns (according to the definition
@@ -2000,16 +2064,35 @@ API int ncplane_putstr_stained(struct ncplane* n, const char* s);
 // (though not beyond the end of the plane); this number is returned on success.
 // On error, a non-positive number is returned, indicating the number of columns
 // which were written before the error. No more than 's' bytes will be written.
-API int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);
+static inline int
+ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters){
+  int ret = 0;
+  int offset = 0;
+//fprintf(stderr, "PUT %zu at %d/%d [%.*s]\n", s, y, x, (int)s, gclusters);
+  while((size_t)offset < s && gclusters[offset]){
+    int wcs;
+    int cols = ncplane_putegc_yx(n, y, x, gclusters + offset, &wcs);
+    if(cols < 0){
+      return -ret;
+    }
+    if(wcs == 0){
+      break;
+    }
+    // after the first iteration, just let the cursor code control where we
+    // print, so that scrolling is taken into account
+    y = -1;
+    x = -1;
+    offset += wcs;
+    ret += cols;
+  }
+  return ret;
+}
 
 static inline int
 ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr){
   return ncplane_putnstr_yx(n, -1, -1, s, gclustarr);
 }
 
-API int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align,
-                                size_t s, const char* gclustarr);
-
 // ncplane_putstr(), but following a conversion from wchar_t to UTF-8 multibyte.
 // FIXME do this as a loop over ncplane_putegc_yx and save the big allocation+copy
 static inline int
@@ -2687,16 +2770,30 @@ nccells_load_box(struct ncplane* n, uint32_t styles, uint64_t channels,
   return -1;
 }
 
-API int nccells_rounded_box(struct ncplane* n, uint16_t styles, uint64_t channels,
-                            nccell* ul, nccell* ur, nccell* ll,
-                            nccell* lr, nccell* hl, nccell* vl);
-
 static inline int
 nccells_ascii_box(struct ncplane* n, uint16_t attr, uint64_t channels,
                   nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
   return nccells_load_box(n, attr, channels, ul, ur, ll, lr, hl, vl, NCBOXASCII);
 }
 
+static inline int
+nccells_double_box(struct ncplane* n, uint16_t attr, uint64_t channels,
+                   nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    return nccells_load_box(n, attr, channels, ul, ur, ll, lr, hl, vl, NCBOXDOUBLE);
+  }
+  return nccells_ascii_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
+}
+
+static inline int
+nccells_rounded_box(struct ncplane* n, uint16_t attr, uint64_t channels,
+                    nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
+  if(notcurses_canutf8(ncplane_notcurses(n))){
+    return nccells_load_box(n, attr, channels, ul, ur, ll, lr, hl, vl, NCBOXROUND);
+  }
+  return nccells_ascii_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
+}
+
 static inline int
 nccells_light_box(struct ncplane* n, uint16_t attr, uint64_t channels,
                   nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
@@ -2764,10 +2861,6 @@ ncplane_rounded_box_sized(struct ncplane* n, uint16_t styles, uint64_t channels,
                              x + xlen - 1, ctlword);
 }
 
-API int nccells_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
-                           nccell* ul, nccell* ur, nccell* ll,
-                           nccell* lr, nccell* hl, nccell* vl);
-
 static inline int
 ncplane_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
                    unsigned ylen, unsigned xlen, unsigned ctlword){
@@ -3000,21 +3093,6 @@ API int ncvisual_set_yx(const struct ncvisual* n, unsigned y, unsigned x,
                         uint32_t pixel)
   __attribute__ ((nonnull (1)));
 
-// Render the decoded frame according to the provided options (which may be
-// NULL). The plane used for rendering depends on vopts->n and vopts->flags. If
-// NCVISUAL_OPTION_CHILDPLANE is set, vopts->n must not be NULL, and the plane
-// will always be created as a child of vopts->n. If this flag is not set, and
-// vopts->n is NULL, a new plane is created as a child of the standard plane.
-// If the flag is not set and vopts->n is not NULL, we render to vopts->n. A
-// subregion of the visual can be rendered using 'begx', 'begy', 'lenx', and
-// 'leny'. Negative values for 'begy' or 'begx' are an error. It is an error to
-// specify any region beyond the boundaries of the frame. Returns the (possibly
-// newly-created) plane to which we drew. Pixels may not be blitted to the
-// standard plane.
-API struct ncplane* ncvisual_render(struct notcurses* nc, struct ncvisual* ncv,
-                                    const struct ncvisual_options* vopts)
-  __attribute__ ((deprecated)) __attribute__ ((nonnull (2)));
-
 // Render the decoded frame according to the provided options (which may be
 // NULL). The plane used for rendering depends on vopts->n and vopts->flags.
 // If NCVISUAL_OPTION_CHILDPLANE is set, vopts->n must not be NULL, and the
@@ -3343,11 +3421,14 @@ API void* nctablet_userptr(struct nctablet* t);
 API struct ncplane* nctablet_plane(struct nctablet* t);
 
 // Takes an arbitrarily large number, and prints it into a fixed-size buffer by
-// adding the necessary SI suffix. Usually, pass a |[IB]PREFIXSTRLEN+1|-sized
-// buffer to generate up to |[IB]PREFIXCOLUMNS| columns' worth of EGCs. The
+// adding the necessary SI suffix. Usually, pass a |NC[IB]?PREFIXSTRLEN+1|-sized
+// buffer to generate up to |NC[IB]?PREFIXCOLUMNS| columns' worth of EGCs. The
 // characteristic can occupy up through |mult-1| characters (3 for 1000, 4 for
 // 1024). The mantissa can occupy either zero or two characters.
-
+//
+// snprintf(3) is used internally, with 's' as its size bound. If the output
+// requires more size than is available, NULL will be returned.
+//
 // Floating-point is never used, because an IEEE758 double can only losslessly
 // represent integers through 2^53-1.
 //
@@ -3355,6 +3436,7 @@ API struct ncplane* nctablet_plane(struct nctablet* t);
 // an 89-bit uintmax_t. Beyond Z(etta) and Y(otta) lie lands unspecified by SI.
 // 2^-63 is 0.000000000000000000108, 1.08a(tto).
 // val: value to print
+// s: maximum output size; see snprintf(3)
 // decimal: scaling. '1' if none has taken place.
 // buf: buffer in which string will be generated
 // omitdec: inhibit printing of all-0 decimal portions
@@ -3363,11 +3445,6 @@ API struct ncplane* nctablet_plane(struct nctablet* t);
 //   only printed if suffix is actually printed (input >= mult).
 //
 // You are encouraged to consult notcurses_metric(3).
-API const char* ncmetric(uintmax_t val, uintmax_t decimal, char* buf,
-                         int omitdec, uintmax_t mult, int uprefix)
-  __attribute__ ((nonnull (3)));
-
-// uses snprintf() internally with the argument 's' as its bound
 API const char* ncnmetric(uintmax_t val, size_t s, uintmax_t decimal,
                           char* buf, int omitdec, uintmax_t mult,
                           int uprefix)
@@ -3376,7 +3453,7 @@ API const char* ncnmetric(uintmax_t val, size_t s, uintmax_t decimal,
 // The number of columns is one fewer, as the STRLEN expressions must leave
 // an extra byte open in case 'µ' (U+00B5, 0xC2 0xB5) shows up. NCPREFIXCOLUMNS
 // is the maximum number of columns used by a mult == 1000 (standard)
-// ncmetric() call. NCIPREFIXCOLUMNS is the maximum number of columns used by a
+// ncnmetric() call. NCIPREFIXCOLUMNS is the maximum number of columns used by a
 // mult == 1024 (digital information) ncmetric(). NCBPREFIXSTRLEN is the maximum
 // number of columns used by a mult == 1024 call making use of the 'i' suffix.
 // This is the true number of columns; to set up a printf()-style maximum
@@ -3397,19 +3474,19 @@ API const char* ncnmetric(uintmax_t val, size_t s, uintmax_t decimal,
 // Mega, kilo, gigafoo. Use PREFIXSTRLEN + 1 and PREFIXCOLUMNS.
 static inline const char*
 ncqprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1000, '\0');
+  return ncnmetric(val, NCPREFIXSTRLEN + 1, decimal, buf, omitdec, 1000, '\0');
 }
 
 // Mibi, kebi, gibibytes sans 'i' suffix. Use IPREFIXSTRLEN + 1.
 static inline const char*
 nciprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1024, '\0');
+  return ncnmetric(val, NCIPREFIXSTRLEN + 1, decimal, buf, omitdec, 1024, '\0');
 }
 
 // Mibi, kebi, gibibytes. Use BPREFIXSTRLEN + 1 and BPREFIXCOLUMNS.
 static inline const char*
 ncbprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1024, 'i');
+  return ncnmetric(val, NCBPREFIXSTRLEN + 1, decimal, buf, omitdec, 1024, 'i');
 }
 
 // Enable or disable the terminal's cursor, if supported, placing it at
@@ -4204,122 +4281,6 @@ API void ncreader_destroy(struct ncreader* n, char** contents);
 API void notcurses_debug(const struct notcurses* nc, FILE* debugfp)
   __attribute__ ((nonnull (1, 2)));
 
-// DEPRECATED MATERIAL, GOING AWAY IN ABI3
-
-__attribute__ ((deprecated)) API int cell_duplicate(struct ncplane* n, nccell* targ, const nccell* c);
-
-__attribute__ ((deprecated)) API void cell_release(struct ncplane* n, nccell* c);
-
-// This function will be removed in ABI3 in favor of ncplane_create().
-// It persists in ABI2 only for backwards compatibility.
-API ALLOC struct ncplane* ncplane_new(struct ncplane* n, int rows, int cols, int y, int x, void* opaque, const char* name)
-  __attribute__ ((deprecated));
-
-API void ncplane_styles_set(struct ncplane* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-API void ncplane_styles_on(struct ncplane* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-API void ncplane_styles_off(struct ncplane* n, unsigned stylebits)
-  __attribute__ ((deprecated));
-
-__attribute__ ((deprecated)) API int
-cells_rounded_box(struct ncplane* n, uint16_t styles, uint64_t channels,
-                  nccell* ul, nccell* ur, nccell* ll,
-                  nccell* lr, nccell* hl, nccell* vl);
-
-__attribute__ ((deprecated)) API int
-cells_double_box(struct ncplane* n, uint16_t styles, uint64_t channels,
-                 nccell* ul, nccell* ur, nccell* ll,
-                 nccell* lr, nccell* hl, nccell* vl);
-
-// Deprecated form of nctablet_plane().
-API struct ncplane* nctablet_ncplane(struct nctablet* t)
-  __attribute__ ((deprecated));
-
-API ALLOC ncpalette* palette256_new(struct notcurses* nc)
-  __attribute__ ((deprecated));
-
-API int palette256_use(struct notcurses* nc, const ncpalette* p)
-  __attribute__ ((deprecated));
-
-API void palette256_free(ncpalette* p) __attribute__ ((deprecated));
-
-// Inflate each pixel in the image to 'scale'x'scale' pixels. It is an error
-// if 'scale' is less than 1. The original color is retained.
-// Deprecated; use ncvisual_resize_noninterpolative(), which this now wraps.
-API __attribute__ ((deprecated)) int ncvisual_inflate(struct ncvisual* n, int scale)
-  __attribute__ ((nonnull (1)));
-
-API int notcurses_render_to_buffer(struct notcurses* nc, char** buf, size_t* buflen)
-  __attribute__ ((deprecated));
-
-API int notcurses_render_to_file(struct notcurses* nc, FILE* fp)
-  __attribute__ ((deprecated));
-
-API void notcurses_debug_caps(const struct notcurses* nc, FILE* debugfp)
-  __attribute__ ((deprecated)) __attribute__ ((nonnull (1, 2)));
-
-__attribute__ ((deprecated)) API int nccell_width(const struct ncplane* n, const nccell* c);
-
-API ALLOC char* ncvisual_subtitle(const struct ncvisual* ncv)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-
-API uint32_t notcurses_getc(struct notcurses* nc, const struct timespec* ts,
-                            const void* unused, ncinput* ni)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-
-API uint32_t ncdirect_getc(struct ncdirect* nc, const struct timespec *ts,
-                           const void* unused, ncinput* ni)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-
-// Get the size and ratio of ncvisual pixels to output cells along the y
-// and x axes. The input size (in pixels) will be written to 'y' and 'x'.
-// The scaling will be written to 'scaley' and 'scalex'. With these:
-//  rows = (y / scaley) + !!(y % scaley) or (y + scaley - 1) / scaley
-//  cols = (x / scalex) + !!(x % scalex) or (x + scalex - 1) / scalex
-// Returns non-zero for an invalid 'vopts'. The blitter that will be used
-// is returned in '*blitter'.
-API int ncvisual_blitter_geom(const struct notcurses* nc, const struct ncvisual* n,
-                              const struct ncvisual_options* vopts, int* y, int* x,
-                              int* scaley, int* scalex, ncblitter_e* blitter)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-
-API int notcurses_mouse_enable(struct notcurses* n)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-API int notcurses_mouse_disable(struct notcurses* n)
-  __attribute__ ((nonnull (1))) __attribute__ ((deprecated));
-
-API int ncplane_highgradient_sized(struct ncplane* n, uint32_t ul, uint32_t ur,
-                                   uint32_t ll, uint32_t lr, int ylen, int xlen)
-  __attribute__ ((deprecated));
-
-__attribute__ ((deprecated)) static inline const char*
-qprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1000, '\0');
-}
-
-// Mibi, kebi, gibibytes sans 'i' suffix. Use IPREFIXSTRLEN + 1.
-__attribute__ ((deprecated)) static inline const char*
-iprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1024, '\0');
-}
-
-// Mibi, kebi, gibibytes. Use BPREFIXSTRLEN + 1 and BPREFIXCOLUMNS.
-__attribute__ ((deprecated)) static inline const char*
-bprefix(uintmax_t val, uintmax_t decimal, char* buf, int omitdec){
-  return ncmetric(val, decimal, buf, omitdec, 1024, 'i');
-}
-
-#define PREFIXCOLUMNS 7
-#define IPREFIXCOLUMNS 8
-#define BPREFIXCOLUMNS 9
-#define PREFIXSTRLEN (PREFIXCOLUMNS + 1)  // Does not include a '\0' (xxx.xxU)
-#define IPREFIXSTRLEN (IPREFIXCOLUMNS + 1) //  Does not include a '\0' (xxxx.xxU)
-#define BPREFIXSTRLEN (BPREFIXCOLUMNS + 1) // Does not include a '\0' (xxxx.xxUi), i == prefix
-#define PREFIXFMT(x) NCMETRICFWIDTH((x), PREFIXCOLUMNS), (x)
-#define IPREFIXFMT(x) NCMETRIXFWIDTH((x), IPREFIXCOLUMNS), (x)
-#define BPREFIXFMT(x) NCMETRICFWIDTH((x), BPREFIXCOLUMNS), (x)
-
 #undef API
 #undef ALLOC
 
diff --git a/python/notcurses/notcurses.py b/python/notcurses/notcurses.py
index f5f2c97277..41b84a82ab 100644
--- a/python/notcurses/notcurses.py
+++ b/python/notcurses/notcurses.py
@@ -524,7 +524,7 @@ def dim_y(self) -> int:
         """Return Y dimension of this NcPlane."""
         raise NotImplementedError('Stub')
 
-    def pixelgeom(self) -> Tuple[int, int, int, int, int, int]:
+    def pixel_geom(self) -> Tuple[int, int, int, int, int, int]:
         """Retrieve pixel geometry for the display region, each cell
         and the maximum displayable bitmap."""
         raise NotImplementedError('Stub')
diff --git a/python/notcurses/plane.c b/python/notcurses/plane.c
index f77f0b45a5..17b400010c 100644
--- a/python/notcurses/plane.c
+++ b/python/notcurses/plane.c
@@ -110,10 +110,10 @@ NcPlane_dim_y(NcPlaneObject *self, PyObject *Py_UNUSED(args))
 }
 
 static PyObject *
-NcPlane_pixelgeom(NcPlaneObject *self, PyObject *Py_UNUSED(args))
+NcPlane_pixel_geom(NcPlaneObject *self, PyObject *Py_UNUSED(args))
 {
     unsigned pxy = 0, pxx = 0, celldimy = 0, celldimx = 0, maxbmapy = 0, maxbmapx = 0;
-    ncplane_pixelgeom(self->ncplane_ptr, &pxy, &pxx, &celldimy, &celldimx, &maxbmapy, &maxbmapx);
+    ncplane_pixel_geom(self->ncplane_ptr, &pxy, &pxx, &celldimy, &celldimx, &maxbmapy, &maxbmapx);
 
     return Py_BuildValue("II II II", pxy, pxx, celldimy, celldimx, maxbmapy, maxbmapx);
 }
@@ -1666,7 +1666,7 @@ static PyMethodDef NcPlane_methods[] = {
     {"dim_yx", (PyCFunction)NcPlane_dim_yx, METH_NOARGS, PyDoc_STR("Return the dimensions of this ncplane.")},
     {"dim_x", (PyCFunction)NcPlane_dim_x, METH_NOARGS, PyDoc_STR("Return X dimension of this ncplane.")},
     {"dim_y", (PyCFunction)NcPlane_dim_y, METH_NOARGS, PyDoc_STR("Return Y dimension of this ncplane.")},
-    {"pixelgeom", (PyCFunction)NcPlane_pixelgeom, METH_NOARGS, PyDoc_STR("Retrieve pixel geometry for the display region ('pxy', 'pxx'), each cell ('celldimy', 'celldimx'), and the maximum displayable bitmap ('maxbmapy', 'maxbmapx'). Note that this will call notcurses_check_pixel_support(), possibly leading to an interrogation of the terminal. If bitmaps are not supported, 'maxbmapy' and 'maxbmapx' will be 0. Any of the geometry arguments may be NULL.")},
+    {"pixel_geom", (PyCFunction)NcPlane_pixel_geom, METH_NOARGS, PyDoc_STR("Retrieve pixel geometry for the display region ('pxy', 'pxx'), each cell ('celldimy', 'celldimx'), and the maximum displayable bitmap ('maxbmapy', 'maxbmapx'). Note that this will call notcurses_check_pixel_support(), possibly leading to an interrogation of the terminal. If bitmaps are not supported, 'maxbmapy' and 'maxbmapx' will be 0. Any of the geometry arguments may be NULL.")},
 
     {"set_resizecb", (PyCFunction)NcPlane_set_resizecb, METH_VARARGS, PyDoc_STR("Replace the ncplane's existing resizecb with 'resizecb' (which may be NULL). The standard plane's resizecb may not be changed.")},
     {"reparent", (PyCFunction)NcPlane_reparent, METH_VARARGS, PyDoc_STR("Plane 'n' will be unbound from its parent plane, and will be made a bound child of 'newparent'.")},
diff --git a/src/compat/compat.h b/src/compat/compat.h
index 81784f5803..0b194dae23 100644
--- a/src/compat/compat.h
+++ b/src/compat/compat.h
@@ -44,8 +44,6 @@ extern "C" {
 typedef struct siginfo_t {
   int aieeee;
 } siginfo_t;
-// not declared in MSYS2 header files, but implemented...?
-int faccessat(int dirfd, const char *pathname, int mode, int flags);
 #define sigset_t int
 #define nl_langinfo(x) "UTF-8"
 #define ppoll(w, x, y, z) WSAPoll((w), (x), (y))
diff --git a/src/demo/boxdemo.c b/src/demo/boxdemo.c
index 687d9cad7d..b043eabe01 100644
--- a/src/demo/boxdemo.c
+++ b/src/demo/boxdemo.c
@@ -130,7 +130,7 @@ get_ships(struct notcurses* nc, struct ship* ships, unsigned shipcount){
     return -1;
   }
   unsigned cdimy, cdimx;
-  ncplane_pixelgeom(notcurses_stdplane(nc), NULL, NULL, &cdimy, &cdimx, NULL, NULL);
+  ncplane_pixel_geom(notcurses_stdplane(nc), NULL, NULL, &cdimy, &cdimx, NULL, NULL);
   if(ncvisual_resize(wmv, cdimy * SHIPHEIGHT, cdimx * SHIPWIDTH)){
     ncvisual_destroy(wmv);
     return -1;
diff --git a/src/demo/demo.c b/src/demo/demo.c
index 86e1a4cf7a..2449bf5bf0 100644
--- a/src/demo/demo.c
+++ b/src/demo/demo.c
@@ -377,7 +377,7 @@ summary_json(FILE* f, const char* spec, int rows, int cols){
       continue;
     }
     ret |= (fprintf(f, "\"%s\":{\"bytes\":\"%"PRIu64"\",\"frames\":\"%"PRIu64"\",\"ns\":\"%"PRIu64"\"}%s",
-                    demos[results[i].selector - 'a'].name, results[i].stats.render_bytes,
+                    demos[results[i].selector - 'a'].name, results[i].stats.raster_bytes,
                     results[i].stats.renders, results[i].timens, i < strlen(spec) - 1 ? "," : "") < 0);
   }
   ret |= (fprintf(f, "}}}\n") < 0);
@@ -411,7 +411,7 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
   for(size_t i = 0 ; i < strlen(spec) ; ++i){
     nsdelta += results[i].timens;
     ncqprefix(results[i].timens, NANOSECS_IN_SEC, timebuf, 0);
-    ncbprefix(results[i].stats.render_bytes, 1, totalbuf, 0);
+    ncbprefix(results[i].stats.raster_bytes, 1, totalbuf, 0);
     uint64_t divisor = results[i].stats.render_ns + results[i].stats.writeout_ns + results[i].stats.raster_ns;
     if(divisor){
       ncqprefix((uintmax_t)results[i].stats.writeouts * NANOSECS_IN_SEC * 1000 / divisor,
@@ -455,7 +455,7 @@ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvid
       failed = true;
     }
     totalframes += results[i].stats.renders;
-    totalbytes += results[i].stats.render_bytes;
+    totalbytes += results[i].stats.raster_bytes;
     totalrenderns += results[i].stats.render_ns;
     totalwriteoutns += results[i].stats.writeout_ns;
   }
diff --git a/src/demo/zoo.c b/src/demo/zoo.c
index 952216b0eb..9e9ad70652 100644
--- a/src/demo/zoo.c
+++ b/src/demo/zoo.c
@@ -14,6 +14,7 @@ draw_background(struct notcurses* nc){
     struct ncvisual_options vopts = {
       .scaling = NCSCALE_STRETCH,
       .n = n,
+      .flags = NCVISUAL_OPTION_CHILDPLANE,
     };
     if(ncvisual_blit(nc, ncv, &vopts) == NULL){
       ncvisual_destroy(ncv);
diff --git a/src/fetch/main.c b/src/fetch/main.c
index 0598acc794..047ac0283c 100644
--- a/src/fetch/main.c
+++ b/src/fetch/main.c
@@ -65,29 +65,6 @@ fetch_env_vars(struct notcurses* nc, fetched_info* fi){
   return 0;
 }
 
-static distro_info distros[] = {
-  {
-    .name = "arch",
-    // from core/filesystem
-    .logofile = "/usr/share/pixmaps/archlinux.png",
-  }, {
-    .name = "artix",
-    // from system/filesystem
-    .logofile = "/usr/share/pixmaps/artixlinux-logo.png",
-  }, {
-    .name = "debian",
-    // from desktop-base package
-    .logofile = "/usr/share/desktop-base/debian-logos/logo-text-256.png",
-  }, {
-    .name = "fedora",
-    // from redhat-lsb-core package
-    .logofile = "/usr/share/pixmaps/fedora-logo.png",
-  }, {
-    .name = NULL,
-    .logofile = NULL,
-  },
-};
-
 static int
 fetch_bsd_cpuinfo(fetched_info* fi){
 #if defined(__linux__) || defined(__gnu_hurd__) || defined(__MINGW64__)
@@ -110,6 +87,32 @@ fetch_bsd_cpuinfo(fetched_info* fi){
   return 0;
 }
 
+static int
+fetch_windows_cpuinfo(fetched_info* fi){
+#ifdef __MINGW64__
+  LPSYSTEM_INFO info;
+  GetSystemInfo(info);
+  switch(info->wProcessorArchitecture){
+    case PROCESSOR_ARCHITECTURE_AMD64:
+      fi->cpu_model = strdup("amd64"); break;
+    case PROCESSOR_ARCHITECTURE_ARM:
+      fi->cpu_model = strdup("ARM"); break;
+    case PROCESSOR_ARCHITECTURE_ARM64:
+      fi->cpu_model = strdup("AArch64"); break;
+    case PROCESSOR_ARCHITECTURE_IA64:
+      fi->cpu_model = strdup("Itanium"); break;
+    case PROCESSOR_ARCHITECTURE_INTEL:
+      fi->cpu_model = strdup("i386"); break;
+    default:
+      fi->cpu_model = strdup("Unknown processor"); break;
+  }
+  fi->core_count = info->dwNumberOfProcessors;
+#else
+  (void)fi;
+#endif
+  return 0;
+}
+
 static int
 fetch_cpu_info(fetched_info* fi){
   FILE* cpuinfo = fopen("/proc/cpuinfo", "re");
@@ -197,6 +200,7 @@ fetch_x_props(fetched_info* fi){
   return 0;
 }
 
+#ifdef __linux__
 // Given a filename, check for its existence in the directories specified by
 // https://specifications.freedesktop.org/icon-theme-spec/latest/ar01s03.html.
 // Returns NULL if no such file can be found. Return value is heap-allocated.
@@ -217,10 +221,35 @@ get_xdg_logo(const char *spec){
   strcat(p, spec);
   return p;
 }
+#endif
 
 // FIXME deal more forgivingly with quotation marks
 static const distro_info*
 linux_ncneofetch(fetched_info* fi){
+  const distro_info* dinfo = NULL;
+#ifdef __linux__
+  static const distro_info distros[] = {
+    {
+      .name = "arch",
+      // from core/filesystem
+      .logofile = "/usr/share/pixmaps/archlinux.png",
+    }, {
+      .name = "artix",
+      // from system/filesystem
+      .logofile = "/usr/share/pixmaps/artixlinux-logo.png",
+    }, {
+      .name = "debian",
+      // from desktop-base package
+      .logofile = "/usr/share/desktop-base/debian-logos/logo-text-256.png",
+    }, {
+      .name = "fedora",
+      // from redhat-lsb-core package
+      .logofile = "/usr/share/pixmaps/fedora-logo.png",
+    }, {
+      .name = NULL,
+      .logofile = NULL,
+    },
+  };
   FILE* osinfo = fopen("/etc/os-release", "re");
   if(osinfo == NULL){
     return NULL;
@@ -266,7 +295,6 @@ linux_ncneofetch(fetched_info* fi){
   if(distro == NULL){
     return NULL;
   }
-  const distro_info* dinfo = NULL;
   for(dinfo = distros ; dinfo->name ; ++dinfo){
     if(strcmp(dinfo->name, distro) == 0){
       break;
@@ -276,6 +304,9 @@ linux_ncneofetch(fetched_info* fi){
     fi->neologo = get_neofetch_art(distro);
   }
   free(distro);
+#else
+  (void)fi;
+#endif
   return dinfo;
 }
 
@@ -309,7 +340,14 @@ get_kernel(fetched_info* fi){
   }
   fprintf(stderr, "Unknown operating system via uname: %s\n", uts.sysname);
 #else
-  (void)fi;
+  OSVERSIONINFO osvi;
+  ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  GetVersionEx(&osvi);
+  char ver[20]; // sure why not
+  snprintf(ver, sizeof(ver), "%lu.%lu", osvi.dwMajorVersion, osvi.dwMinorVersion);
+  fi->kernver = strdup(ver);
+  fi->kernel = strdup("Windows NT");
   return NCNEO_WINDOWS;
 #endif
   return NCNEO_UNKNOWN;
@@ -440,7 +478,7 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi, int planeheigh
   size_t oldlenp = sizeof(ram);
   if(sysctlbyname("hw.memsize", &ram, &oldlenp, NULL, 0) == 0){
     char tram[NCBPREFIXSTRLEN + 1];
-    bprefix(ram, 1, tram, 1);
+    ncbprefix(ram, 1, tram, 1);
     ncplane_printf_aligned(infop, 2, NCALIGN_LEFT, " RAM: %sB", tram);
   }
 #endif
@@ -664,6 +702,8 @@ ncneofetch(struct notcurses* nc){
   }
   if(kern == NCNEO_LINUX){
     fetch_cpu_info(&fi);
+  }else if(kern == NCNEO_WINDOWS){
+    fetch_windows_cpuinfo(&fi);
   }else{
     fetch_bsd_cpuinfo(&fi);
   }
diff --git a/src/info/main.c b/src/info/main.c
index 67eeab89c4..90dea2663a 100644
--- a/src/info/main.c
+++ b/src/info/main.c
@@ -331,7 +331,7 @@ unicodedumper(struct ncplane* n, const char* indent){
 static int
 display_logo(struct ncplane* n, const char* path){
   unsigned cpixy, cpixx;
-  ncplane_pixelgeom(n, NULL, NULL, &cpixy, &cpixx, NULL, NULL);
+  ncplane_pixel_geom(n, NULL, NULL, &cpixy, &cpixx, NULL, NULL);
   struct ncvisual* ncv = ncvisual_from_file(path);
   if(ncv == NULL){
     return -1;
diff --git a/src/input/input.cpp b/src/input/input.cpp
index 1d252ba7cd..bd787fbfdd 100644
--- a/src/input/input.cpp
+++ b/src/input/input.cpp
@@ -255,7 +255,7 @@ int input_demo(ncpp::NotCurses* nc) {
   auto n = nc->get_stdplane(&dimy, &dimx);
   // FIXME no ncpp wrapper for Plane::pixelgeom?
   unsigned celldimx, maxbmapx;
-  ncplane_pixelgeom(*n, nullptr, nullptr, nullptr, &celldimx, nullptr, &maxbmapx);
+  ncplane_pixel_geom(*n, nullptr, nullptr, nullptr, &celldimx, nullptr, &maxbmapx);
   struct ncplane_options nopts = {
     .y = static_cast<int>(dimy) - PLOTHEIGHT - 1,
     .x = NCALIGN_CENTER,
diff --git a/src/lib/debug.c b/src/lib/debug.c
index 6724fe9793..dc86796c7c 100644
--- a/src/lib/debug.c
+++ b/src/lib/debug.c
@@ -2,12 +2,6 @@
 
 int loglevel = NCLOGLEVEL_SILENT;
 
-void notcurses_debug_caps(const notcurses* nc, FILE* debugfp){
-  // FIXME deprecated, remove for ABI3
-  (void)nc;
-  (void)debugfp;
-}
-
 void notcurses_debug(const notcurses* nc, FILE* debugfp){
   fbuf f;
   if(fbuf_init_small(&f)){
diff --git a/src/lib/direct.c b/src/lib/direct.c
index 4dd94d13ac..ea14bfa901 100644
--- a/src/lib/direct.c
+++ b/src/lib/direct.c
@@ -1124,11 +1124,6 @@ ncdirect_style_emit(ncdirect* n, unsigned stylebits, fbuf* f){
   return r;
 }
 
-// turn on any specified stylebits
-int ncdirect_styles_on(ncdirect* n, unsigned stylebits){
-  return ncdirect_on_styles(n, stylebits);
-}
-
 int ncdirect_on_styles(ncdirect* n, unsigned stylebits){
   if((stylebits & n->tcache.supported_styles) < stylebits){ // unsupported styles
     return -1;
@@ -1148,11 +1143,7 @@ int ncdirect_on_styles(ncdirect* n, unsigned stylebits){
   return 0;
 }
 
-int ncdirect_styles_off(ncdirect* n, unsigned stylebits){
-  return ncdirect_off_styles(n, stylebits);
-}
-
-unsigned ncdirect_styles(const ncdirect* n){
+uint16_t ncdirect_styles(const ncdirect* n){
   return n->stylemask;
 }
 
@@ -1173,10 +1164,6 @@ int ncdirect_off_styles(ncdirect* n, unsigned stylebits){
   return 0;
 }
 
-int ncdirect_styles_set(ncdirect* n, unsigned stylebits){
-  return ncdirect_set_styles(n, stylebits);
-}
-
 // set the current stylebits to exactly those provided
 int ncdirect_set_styles(ncdirect* n, unsigned stylebits){
   if((stylebits & n->tcache.supported_styles) < stylebits){ // unsupported styles
@@ -1595,7 +1582,7 @@ int ncdirectf_geom(ncdirect* n, ncdirectf* frame,
                              &placey, &placex);
 }
 
-unsigned ncdirect_supported_styles(const ncdirect* nc){
+uint16_t ncdirect_supported_styles(const ncdirect* nc){
   return term_supported_styles(&nc->tcache);
 }
 
diff --git a/src/lib/fd.c b/src/lib/fd.c
index b70052b996..965c83679d 100644
--- a/src/lib/fd.c
+++ b/src/lib/fd.c
@@ -233,11 +233,11 @@ launch_pipe_process(int* pipefd, int* pidfd, unsigned usepath,
 }
 #endif
 
+#ifndef __MINGW64__
 // nuke the just-spawned process, and reap it. called before the subprocess
 // reader thread is launched (which otherwise reaps the subprocess).
 static int
 kill_and_wait_subproc(pid_t pid, int pidfd, int* status){
-#ifndef __MINGW64__
   int ret = -1;
   // on linux, we try pidfd_send_signal, if the pidfd has been defined.
   // otherwise, we fall back to regular old kill();
@@ -258,12 +258,6 @@ kill_and_wait_subproc(pid_t pid, int pidfd, int* status){
     return -1;
   }
   return 0;
-#else
-  (void)pid;
-  (void)pidfd;
-  (void)status;
-  return -1;
-#endif
 }
 
 // need a poll on both main fd and pidfd
@@ -335,6 +329,7 @@ ncsubproc_launch(ncplane* n, ncsubproc* ret, const ncsubproc_options* opts, int
   }
   return ret->nfp;
 }
+#endif
 
 // use of env implies usepath
 static ncsubproc*
diff --git a/src/lib/in.c b/src/lib/in.c
index 25102cc7d2..12f3184b9b 100644
--- a/src/lib/in.c
+++ b/src/lib/in.c
@@ -502,11 +502,11 @@ mouse_click(inputctx* ictx, unsigned release, char follow){
     logwarn("dropping click in margins %ld/%ld\n", y, x);
     return;
   }
-  if(x >= ictx->ti->dimx - (ictx->rmargin + ictx->lmargin)){
+  if((unsigned)x >= ictx->ti->dimx - (ictx->rmargin + ictx->lmargin)){
     logwarn("dropping click in margins %ld/%ld\n", y, x);
     return;
   }
-  if(y >= ictx->ti->dimy - (ictx->bmargin + ictx->tmargin)){
+  if((unsigned)y >= ictx->ti->dimy - (ictx->bmargin + ictx->tmargin)){
     logwarn("dropping click in margins %ld/%ld\n", y, x);
     return;
   }
@@ -2200,18 +2200,6 @@ uint32_t ncdirect_get(ncdirect* n, const struct timespec* ts, ncinput* ni){
   return internal_get(n->tcache.ictx, ts, ni);
 }
 
-uint32_t notcurses_getc(notcurses* nc, const struct timespec* ts,
-                        const void* unused, ncinput* ni){
-  (void)unused; // FIXME remove for abi3
-  return notcurses_get(nc, ts, ni);
-}
-
-uint32_t ncdirect_getc(ncdirect* nc, const struct timespec *ts,
-                       const void* unused, ncinput* ni){
-  (void)unused; // FIXME remove for abi3
-  return ncdirect_get(nc, ts, ni);
-}
-
 int get_cursor_location(inputctx* ictx, const char* u7, unsigned* y, unsigned* x){
   pthread_mutex_lock(&ictx->clock);
   while(ictx->cvalid == 0){
diff --git a/src/lib/internal.h b/src/lib/internal.h
index 4b98deb136..ffa72557a8 100644
--- a/src/lib/internal.h
+++ b/src/lib/internal.h
@@ -393,7 +393,7 @@ void summarize_stats(notcurses* nc);
 
 void update_raster_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats);
 void update_render_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats);
-void update_render_bytes(ncstats* stats, int bytes);
+void update_raster_bytes(ncstats* stats, int bytes);
 void update_write_stats(const struct timespec* time1, const struct timespec* time0, ncstats* stats, int bytes);
 
 void sigwinch_handler(int signo);
diff --git a/src/lib/metric.c b/src/lib/metric.c
index aab43de5d6..ab78ed7511 100644
--- a/src/lib/metric.c
+++ b/src/lib/metric.c
@@ -116,8 +116,3 @@ const char* ncnmetric(uintmax_t val, size_t s, uintmax_t decimal,
   }
   return buf;
 }
-
-const char *ncmetric(uintmax_t val, uintmax_t decimal, char *buf, int omitdec,
-                     uintmax_t mult, int uprefix){
-  return ncnmetric(val, INT_MAX, decimal, buf, omitdec, mult, uprefix);
-}
diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c
index 77b9302bf6..455bdd0841 100644
--- a/src/lib/notcurses.c
+++ b/src/lib/notcurses.c
@@ -125,35 +125,6 @@ notcurses_stop_minimal(void* vnc){
   return ret;
 }
 
-// make a heap-allocated wchar_t expansion of the multibyte string at s
-wchar_t* wchar_from_utf8(const char* s){
-  mbstate_t ps;
-  memset(&ps, 0, sizeof(ps));
-  // worst case is a wchar_t for every byte of input (all-ASCII case)
-  const size_t maxw = strlen(s) + 1;
-  wchar_t* dst = malloc(sizeof(*dst) * maxw);
-  size_t wcount = mbsrtowcs(dst, &s, maxw, &ps);
-  if(wcount == (size_t)-1){
-    free(dst);
-    return NULL;
-  }
-  if(s){
-    free(dst);
-    return NULL;
-  }
-  return dst;
-}
-
-int ncplane_putstr_aligned(ncplane* n, int y, ncalign_e align, const char* s){
-  wchar_t* w = wchar_from_utf8(s);
-  if(w == NULL){
-    return -1;
-  }
-  int r = ncplane_putwstr_aligned(n, y, align, w);
-  free(w);
-  return r;
-}
-
 static const char NOTCURSES_VERSION[] =
  NOTCURSES_VERSION_MAJOR "."
  NOTCURSES_VERSION_MINOR "."
@@ -605,20 +576,6 @@ ncplane* ncpile_create(notcurses* nc, const struct ncplane_options* nopts){
   return ncplane_new_internal(nc, NULL, nopts);
 }
 
-struct ncplane* ncplane_new(struct ncplane* n, int rows, int cols, int y, int x, void* opaque, const char* name){
-  ncplane_options nopts = {
-    .y = y,
-    .x = x,
-    .rows = rows,
-    .cols = cols,
-    .userptr = opaque,
-    .name = name,
-    .resizecb = NULL,
-    .flags = 0,
-  };
-  return ncplane_create(n, &nopts);
-}
-
 void ncplane_home(ncplane* n){
   n->x = 0;
   n->y = 0;
@@ -1383,32 +1340,44 @@ const char* cell_extended_gcluster(const struct ncplane* n, const nccell* c){
 
 // 'n' ends up above 'above'
 int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){
-  if(n == above){
+  if(n == above){ // probably gets optimized out =/
     return -1;
   }
+  ncpile* p = ncplane_pile(n);
   if(above == NULL){
-    ncplane_move_bottom(n);
+    if(n->below){
+      if( (n->below->above = n->above) ){
+        n->above->below = n->below;
+      }else{
+        p->top = n->below;
+      }
+      n->below = NULL;
+      if( (n->above = p->bottom) ){
+        n->above->below = n;
+      }
+      p->bottom = n;
+    }
     return 0;
   }
-  if(ncplane_pile(n) != ncplane_pile(above)){ // can't move among piles
-    return -1;
-  }
   if(n->below != above){
+    if(p != ncplane_pile(above)){ // can't move among piles
+      return -1;
+    }
     // splice out 'n'
     if(n->below){
       n->below->above = n->above;
     }else{
-      ncplane_pile(n)->bottom = n->above;
+      p->bottom = n->above;
     }
     if(n->above){
       n->above->below = n->below;
     }else{
-      ncplane_pile(n)->top = n->below;
+      p->top = n->below;
     }
     if( (n->above = above->above) ){
       above->above->below = n;
     }else{
-      ncplane_pile(n)->top = n;
+      p->top = n;
     }
     above->above = n;
     n->below = above;
@@ -1416,33 +1385,45 @@ int ncplane_move_above(ncplane* restrict n, ncplane* restrict above){
   return 0;
 }
 
-// 'n' ends up below 'below'
+// 'n' ends up below 'below', or on top if 'below' == NULL
 int ncplane_move_below(ncplane* restrict n, ncplane* restrict below){
-  if(n == below){
+  if(n == below){ // probably gets optimized out =/
     return -1;
   }
+  ncpile* p = ncplane_pile(n);
   if(below == NULL){
-    ncplane_move_top(n);
+    if(n->above){
+      if( (n->above->below = n->below) ){
+        n->below->above = n->above;
+      }else{
+        p->bottom = n->above;
+      }
+      n->above = NULL;
+      if( (n->below = p->top) ){
+        n->below->above = n;
+      }
+      p->top = n;
+    }
     return 0;
   }
-  if(ncplane_pile(n) != ncplane_pile(below)){ // can't move among piles
-    return -1;
-  }
   if(n->above != below){
+    if(p != ncplane_pile(below)){ // can't move among piles
+      return -1;
+    }
     if(n->below){
       n->below->above = n->above;
     }else{
-      ncplane_pile(n)->bottom = n->above;
+      p->bottom = n->above;
     }
     if(n->above){
       n->above->below = n->below;
     }else{
-      ncplane_pile(n)->top = n->below;
+      p->top = n->below;
     }
     if( (n->below = below->below) ){
       below->below->above = n;
     }else{
-      ncplane_pile(n)->bottom = n;
+      p->bottom = n;
     }
     below->below = n;
     n->above = below;
@@ -1450,36 +1431,6 @@ int ncplane_move_below(ncplane* restrict n, ncplane* restrict below){
   return 0;
 }
 
-void ncplane_move_top(ncplane* n){
-  if(n->above){
-    if( (n->above->below = n->below) ){
-      n->below->above = n->above;
-    }else{
-      ncplane_pile(n)->bottom = n->above;
-    }
-    n->above = NULL;
-    if( (n->below = ncplane_pile(n)->top) ){
-      n->below->above = n;
-    }
-    ncplane_pile(n)->top = n;
-  }
-}
-
-void ncplane_move_bottom(ncplane* n){
-  if(n->below){
-    if( (n->below->above = n->above) ){
-      n->above->below = n->below;
-    }else{
-      ncplane_pile(n)->top = n->below;
-    }
-    n->below = NULL;
-    if( (n->above = ncplane_pile(n)->bottom) ){
-      n->above->below = n;
-    }
-    ncplane_pile(n)->bottom = n;
-  }
-}
-
 // if above is NULL, we're moving to the bottom
 int ncplane_move_family_above(ncplane* restrict n, ncplane* restrict bpoint){
   ncplane* above = ncplane_above(n);
@@ -1584,7 +1535,7 @@ void scroll_down(ncplane* n){
   for(struct ncplane* c = n->blist ; c ; c = c->bnext){
     if(!c->fixedbound){
       if(ncplanes_intersect_p(n, c)){
-        ncplane_moverel(c, -1, 0);
+        ncplane_move_rel(c, -1, 0);
       }
     }
   }
@@ -1627,10 +1578,6 @@ int ncplane_scrollup_child(ncplane* n, const ncplane* child){
   return ret;
 }
 
-int nccell_width(const ncplane* n __attribute__ ((unused)), const nccell* c){
-  return nccell_cols(c);
-}
-
 int nccell_load(ncplane* n, nccell* c, const char* gcluster){
   int cols;
   int bytes = utf8_egc_len(gcluster, &cols);
@@ -1787,7 +1734,7 @@ int ncplane_putegc_stained(ncplane* n, const char* gclust, int* sbytes){
 }
 
 int ncplane_cursor_at(const ncplane* n, nccell* c, char** gclust){
-  if(n->y == n->leny && n->x == n->lenx){
+  if(n->y >= n->leny || n->x >= n->lenx){
     return -1;
   }
   const nccell* src = &n->fb[nfbcellidx(n, n->y, n->x)];
@@ -1800,7 +1747,7 @@ int ncplane_cursor_at(const ncplane* n, nccell* c, char** gclust){
   return 0;
 }
 
-unsigned notcurses_supported_styles(const notcurses* nc){
+uint16_t notcurses_supported_styles(const notcurses* nc){
   return term_supported_styles(&nc->tcache);
 }
 
@@ -1821,28 +1768,16 @@ void ncplane_set_styles(ncplane* n, unsigned stylebits){
   n->stylemask = (stylebits & NCSTYLE_MASK);
 }
 
-void ncplane_styles_set(ncplane* n, unsigned stylebits){ // deprecated
-  ncplane_set_styles(n, stylebits);
-}
-
 // turn on any specified stylebits
 void ncplane_on_styles(ncplane* n, unsigned stylebits){
   n->stylemask |= (stylebits & NCSTYLE_MASK);
 }
 
-void ncplane_styles_on(ncplane* n, unsigned stylebits){ // deprecated
-  ncplane_on_styles(n, stylebits);
-}
-
 // turn off any specified stylebits
 void ncplane_off_styles(ncplane* n, unsigned stylebits){
   n->stylemask &= ~(stylebits & NCSTYLE_MASK);
 }
 
-void ncplane_styles_off(ncplane* n, unsigned stylebits){ // deprecated
-  ncplane_off_styles(n, stylebits);
-}
-
 // i hate the big allocation and two copies here, but eh what you gonna do?
 // well, for one, we don't need the huge allocation FIXME
 char* ncplane_vprintf_prep(const char* format, va_list ap){
@@ -1904,6 +1839,13 @@ int ncplane_vprintf_stained(struct ncplane* n, const char* format, va_list ap){
   return ret;
 }
 
+int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* str){
+  char* chopped = strndup(str, s);
+  int ret = ncplane_putstr_aligned(n, y, align, chopped);
+  free(chopped);
+  return ret;
+}
+
 int ncplane_hline_interp(ncplane* n, const nccell* c, unsigned len,
                          uint64_t c1, uint64_t c2){
   if(len <= 0){
@@ -2370,10 +2312,6 @@ ncpalette* ncpalette_new(notcurses* nc){
   return p;
 }
 
-ncpalette* palette256_new(notcurses* nc){
-  return ncpalette_new(nc);
-}
-
 int ncpalette_use(notcurses* nc, const ncpalette* p){
   int ret = -1;
   if(!notcurses_canchangecolor(nc)){
@@ -2389,18 +2327,10 @@ int ncpalette_use(notcurses* nc, const ncpalette* p){
   return ret;
 }
 
-int palette256_use(notcurses* nc, const ncpalette* p){
-  return ncpalette_use(nc, p);
-}
-
 void ncpalette_free(ncpalette* p){
   free(p);
 }
 
-void palette256_free(ncpalette* p){
-  ncpalette_free(p);
-}
-
 bool ncplane_translate_abs(const ncplane* n, int* restrict y, int* restrict x){
   ncplane_translate(ncplane_stdplane_const(n), n, y, x);
   if(y){
@@ -3006,32 +2936,6 @@ char* ncplane_contents(ncplane* nc, int begy, int begx, unsigned leny, unsigned
   return ret;
 }
 
-int nccells_double_box(ncplane* n, uint16_t attr, uint64_t channels,
-                       nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
-  if(notcurses_canutf8(ncplane_notcurses(n))){
-    return nccells_load_box(n, attr, channels, ul, ur, ll, lr, hl, vl, NCBOXDOUBLE);
-  }
-  return nccells_ascii_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
-}
-
-int cells_double_box(ncplane* n, uint16_t attr, uint64_t channels,
-                     nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
-  return nccells_double_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
-}
-
-int nccells_rounded_box(ncplane* n, uint16_t attr, uint64_t channels,
-                        nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
-  if(notcurses_canutf8(ncplane_notcurses(n))){
-    return nccells_load_box(n, attr, channels, ul, ur, ll, lr, hl, vl, NCBOXROUND);
-  }
-  return nccells_ascii_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
-}
-
-int cells_rounded_box(ncplane* n, uint16_t attr, uint64_t channels,
-                      nccell* ul, nccell* ur, nccell* ll, nccell* lr, nccell* hl, nccell* vl){
-  return nccells_rounded_box(n, attr, channels, ul, ur, ll, lr, hl, vl);
-}
-
 // find the center coordinate of a plane, preferring the top/left in the
 // case of an even number of rows/columns (in such a case, there will be one
 // more cell to the bottom/right of the center than the top/left). the
@@ -3053,45 +2957,6 @@ void nclog(const char* fmt, ...){
   va_end(va);
 }
 
-int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){
-  int ret = 0;
-  while(*gclusters){
-    int wcs;
-    int cols = ncplane_putegc_yx(n, y, x, gclusters, &wcs);
-//fprintf(stderr, "wrote %.*s %d cols %d bytes now at %d/%d\n", wcs, gclusters, cols, wcs, n->y, n->x);
-    if(cols < 0){
-      return -ret;
-    }
-    if(wcs == 0){
-      break;
-    }
-    // after the first iteration, just let the cursor code control where we
-    // print, so that scrolling is taken into account
-    y = -1;
-    x = -1;
-    gclusters += wcs;
-    ret += cols;
-  }
-  return ret;
-}
-
-int ncplane_putstr_stained(struct ncplane* n, const char* gclusters){
-  int ret = 0;
-  while(*gclusters){
-    int wcs;
-    int cols = ncplane_putegc_stained(n, gclusters, &wcs);
-    if(cols < 0){
-      return -ret;
-    }
-    if(wcs == 0){
-      break;
-    }
-    gclusters += wcs;
-    ret += cols;
-  }
-  return ret;
-}
-
 int ncplane_putwstr_stained(ncplane* n, const wchar_t* gclustarr){
   mbstate_t ps = {};
   const wchar_t** wset = &gclustarr;
@@ -3115,36 +2980,6 @@ int ncplane_putwstr_stained(ncplane* n, const wchar_t* gclustarr){
   return r;
 }
 
-int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* str){
-  char* chopped = strndup(str, s);
-  int ret = ncplane_putstr_aligned(n, y, align, chopped);
-  free(chopped);
-  return ret;
-}
-
-int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters){
-  int ret = 0;
-  int offset = 0;
-//fprintf(stderr, "PUT %zu at %d/%d [%.*s]\n", s, y, x, (int)s, gclusters);
-  while((size_t)offset < s && gclusters[offset]){
-    int wcs;
-    int cols = ncplane_putegc_yx(n, y, x, gclusters + offset, &wcs);
-    if(cols < 0){
-      return -ret;
-    }
-    if(wcs == 0){
-      break;
-    }
-    // after the first iteration, just let the cursor code control where we
-    // print, so that scrolling is taken into account
-    y = -1;
-    x = -1;
-    offset += wcs;
-    ret += cols;
-  }
-  return ret;
-}
-
 int notcurses_ucs32_to_utf8(const uint32_t* ucs32, unsigned ucs32count,
                             unsigned char* resultbuf, size_t buflen){
   if(u32_to_u8(ucs32, ucs32count, resultbuf, &buflen) == NULL){
@@ -3153,19 +2988,17 @@ int notcurses_ucs32_to_utf8(const uint32_t* ucs32, unsigned ucs32count,
   return buflen;
 }
 
-int ncstrwidth(const char* mbs){
-  return ncstrwidth_valid(mbs, NULL, NULL);
-}
-
 int ncstrwidth_valid(const char* egcs, int* validbytes, int* validwidth){
-  int cols = 0;  // number of columns consumed thus far
+  int cols;
   if(validwidth == NULL){
     validwidth = &cols;
   }
-  int bytes = 0; // number of bytes consumed thus far
+  *validwidth = 0;
+  int bytes;
   if(validbytes == NULL){
     validbytes = &bytes;
   }
+  *validbytes = 0;
   do{
     int thesecols, thesebytes;
     thesebytes = utf8_egc_len(egcs, &thesecols);
@@ -3179,9 +3012,10 @@ int ncstrwidth_valid(const char* egcs, int* validbytes, int* validwidth){
   return *validwidth;
 }
 
-void ncplane_pixelgeom(const ncplane* n, unsigned* RESTRICT pxy, unsigned* RESTRICT pxx,
-                       unsigned* RESTRICT celldimy, unsigned* RESTRICT celldimx,
-                       unsigned* RESTRICT maxbmapy, unsigned* RESTRICT maxbmapx){
+void ncplane_pixel_geom(const ncplane* n,
+                        unsigned* RESTRICT pxy, unsigned* RESTRICT pxx,
+                        unsigned* RESTRICT celldimy, unsigned* RESTRICT celldimx,
+                        unsigned* RESTRICT maxbmapy, unsigned* RESTRICT maxbmapx){
   notcurses* nc = ncplane_notcurses(n);
   if(celldimy){
     *celldimy = nc->tcache.cellpixy;
diff --git a/src/lib/plot.c b/src/lib/plot.c
index d6b69dee7f..5cd5975a47 100644
--- a/src/lib/plot.c
+++ b/src/lib/plot.c
@@ -127,12 +127,12 @@ int redraw_pixelplot_##T(nc##X##plot* ncp){ \
       char buf[NCPREFIXSTRLEN + 1]; \
       if(ncp->plot.exponentiali){ \
         if(y == dimy - 1){ /* we cheat on the top row to exactly match maxy */ \
-          ncmetric(ncp->maxy * 100, 100, buf, 0, 1000, '\0'); \
+          ncqprefix(ncp->maxy * 100, 100, buf, 0); \
         }else{ \
-          ncmetric(pow(interval, (y + 1) * states) * 100, 100, buf, 0, 1000, '\0'); \
+          ncqprefix(pow(interval, (y + 1) * states) * 100, 100, buf, 0); \
         } \
       }else{ \
-        ncmetric((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0, 1000, '\0'); \
+        ncqprefix((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0); \
       } \
       if(y == dimy - 1 && strlen(ncp->plot.title)){ \
         ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, 0, "%*.*s %s", \
@@ -297,12 +297,12 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
       char buf[NCPREFIXSTRLEN + 1]; \
       if(ncp->plot.exponentiali){ \
         if(y == dimy - 1){ /* we cheat on the top row to exactly match maxy */ \
-          ncmetric(ncp->maxy * 100, 100, buf, 0, 1000, '\0'); \
+          ncqprefix(ncp->maxy * 100, 100, buf, 0); \
         }else{ \
-          ncmetric(pow(interval, (y + 1) * states) * 100, 100, buf, 0, 1000, '\0'); \
+          ncqprefix(pow(interval, (y + 1) * states) * 100, 100, buf, 0); \
         } \
       }else{ \
-        ncmetric((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0, 1000, '\0'); \
+        ncqprefix((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0); \
       } \
       if(y == dimy - 1 && strlen(ncp->plot.title)){ \
         ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, NCPREFIXCOLUMNS - strlen(buf), "%s %s", buf, ncp->plot.title); \
diff --git a/src/lib/reel.c b/src/lib/reel.c
index a4ab71cdde..8a5e0efe3e 100644
--- a/src/lib/reel.c
+++ b/src/lib/reel.c
@@ -754,10 +754,6 @@ ncplane* nctablet_plane(nctablet* t){
   return t->cbp;
 }
 
-ncplane* nctablet_ncplane(nctablet* t){ // deprecated
-  return nctablet_plane(t);
-}
-
 ncplane* ncreel_plane(ncreel* nr){
   return nr->p;
 }
diff --git a/src/lib/render.c b/src/lib/render.c
index 0fd160d04f..ef8917cb3d 100644
--- a/src/lib/render.c
+++ b/src/lib/render.c
@@ -122,11 +122,6 @@ void nccell_release(ncplane* n, nccell* c){
   pool_release(&n->pool, c);
 }
 
-// FIXME deprecated, goes away in abi3
-void cell_release(ncplane* n, nccell* c){
-  nccell_release(n, c);
-}
-
 // Duplicate one cell onto another when they share a plane. Convenience wrapper.
 int nccell_duplicate(ncplane* n, nccell* targ, const nccell* c){
   if(cell_duplicate_far(&n->pool, targ, n, c) < 0){
@@ -136,11 +131,6 @@ int nccell_duplicate(ncplane* n, nccell* targ, const nccell* c){
   return 0;
 }
 
-// deprecated, goes away in abi3
-int cell_duplicate(struct ncplane* n, nccell* targ, const nccell* c){
-  return nccell_duplicate(n, targ, c);
-}
-
 // Emit fchannel with RGB changed to contrast effectively against bchannel.
 static uint32_t
 highcontrast(const tinfo* ti, uint32_t bchannel){
@@ -1458,10 +1448,6 @@ int ncpile_render_to_file(ncplane* n, FILE* fp){
   return ret;
 }
 
-int notcurses_render_to_file(notcurses* nc, FILE* fp){
-  return ncpile_render_to_file(notcurses_stdplane(nc), fp);
-}
-
 // We execute the painter's algorithm, starting from our topmost plane. The
 // damagevector should be all zeros on input. On success, it will reflect
 // which cells were changed. We solve for each coordinate's cell by walking
@@ -1505,7 +1491,7 @@ int ncpile_rasterize(ncplane* n){
   // accepts -1 as an indication of failure
   clock_gettime(CLOCK_MONOTONIC, &writedone);
   pthread_mutex_lock(&nc->stats.lock);
-    update_render_bytes(&nc->stats.s, bytes);
+    update_raster_bytes(&nc->stats.s, bytes);
     update_raster_stats(&rasterdone, &start, &nc->stats.s);
     update_write_stats(&writedone, &rasterdone, &nc->stats.s, bytes);
   pthread_mutex_unlock(&nc->stats.lock);
@@ -1556,18 +1542,6 @@ int ncpile_render(ncplane* n){
   return 0;
 }
 
-int notcurses_render(notcurses* nc){
-//fprintf(stderr, "--------------- BEGIN RENDER\n");
-//notcurses_debug(nc, stderr);
-  ncplane* stdn = notcurses_stdplane(nc);
-  if(ncpile_render(stdn)){
-    return -1;
-  }
-  int i = ncpile_rasterize(stdn);
-//fprintf(stderr, "----------------- END RENDER\n");
-  return i;
-}
-
 // run the top half of notcurses_render(), and steal the buffer from rstate.
 int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
   if(ncpile_render(p)){
@@ -1578,7 +1552,7 @@ int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
   fbuf_reset(&nc->rstate.f);
   int bytes = notcurses_rasterize_inner(nc, ncplane_pile(p), &nc->rstate.f, &useasu);
   pthread_mutex_lock(&nc->stats.lock);
-    update_render_bytes(&nc->stats.s, bytes);
+    update_raster_bytes(&nc->stats.s, bytes);
   pthread_mutex_unlock(&nc->stats.lock);
   if(bytes < 0){
     return -1;
@@ -1589,10 +1563,6 @@ int ncpile_render_to_buffer(ncplane* p, char** buf, size_t* buflen){
   return 0;
 }
 
-int notcurses_render_to_buffer(notcurses* nc, char** buf, size_t* buflen){
-  return ncpile_render_to_buffer(notcurses_stdplane(nc), buf, buflen);
-}
-
 // copy the UTF8-encoded EGC out of the cell, whether simple or complex. the
 // result is not tied to the ncplane, and persists across erases / destruction.
 static inline char*
diff --git a/src/lib/stats.c b/src/lib/stats.c
index eb8511a174..a05193d18c 100644
--- a/src/lib/stats.c
+++ b/src/lib/stats.c
@@ -22,16 +22,16 @@ void update_write_stats(const struct timespec* time1, const struct timespec* tim
 }
 
 // negative 'bytes' are ignored as failures. call only while holding statlock.
-// we don't increment failed_renders here because 'bytes' < 0 actually indicates
-// a rasterization failure -- we can't fail in rendering anymore.
-void update_render_bytes(ncstats* stats, int bytes){
+// we don't increment failed_rasters here because 'bytes' < 0 actually indicates
+// a rasterization failure -- we can't fail in rastering anymore.
+void update_raster_bytes(ncstats* stats, int bytes){
   if(bytes >= 0){
-    stats->render_bytes += bytes;
-    if(bytes > stats->render_max_bytes){
-      stats->render_max_bytes = bytes;
+    stats->raster_bytes += bytes;
+    if(bytes > stats->raster_max_bytes){
+      stats->raster_max_bytes = bytes;
     }
-    if(bytes < stats->render_min_bytes){
-      stats->render_min_bytes = bytes;
+    if(bytes < stats->raster_min_bytes){
+      stats->raster_min_bytes = bytes;
     }
   }
 }
@@ -76,7 +76,7 @@ void reset_stats(ncstats* stats){
   unsigned planes = stats->planes;
   memset(stats, 0, sizeof(*stats));
   stats->render_min_ns = 1ull << 62u;
-  stats->render_min_bytes = 1ull << 62u;
+  stats->raster_min_bytes = 1ull << 62u;
   stats->raster_min_ns = 1ull << 62u;
   stats->writeout_min_ns = 1ull << 62u;
   stats->fbbytes = fbbytes;
@@ -108,8 +108,8 @@ void notcurses_stats_reset(notcurses* nc, ncstats* stats){
     if(nc->stats.s.render_min_ns < stash->render_min_ns){
       stash->render_min_ns = nc->stats.s.render_min_ns;
     }
-    if(nc->stats.s.render_min_bytes < stash->render_min_bytes){
-      stash->render_min_bytes = nc->stats.s.render_min_bytes;
+    if(nc->stats.s.raster_min_bytes < stash->raster_min_bytes){
+      stash->raster_min_bytes = nc->stats.s.raster_min_bytes;
     }
     if(nc->stats.s.raster_min_ns < stash->raster_min_ns){
       stash->raster_min_ns = nc->stats.s.raster_min_ns;
@@ -120,8 +120,8 @@ void notcurses_stats_reset(notcurses* nc, ncstats* stats){
     if(nc->stats.s.render_max_ns > stash->render_max_ns){
       stash->render_max_ns = nc->stats.s.render_max_ns;
     }
-    if(nc->stats.s.render_max_bytes > stash->render_max_bytes){
-      stash->render_max_bytes = nc->stats.s.render_max_bytes;
+    if(nc->stats.s.raster_max_bytes > stash->raster_max_bytes){
+      stash->raster_max_bytes = nc->stats.s.raster_max_bytes;
     }
     if(nc->stats.s.raster_max_ns > stash->raster_max_ns){
       stash->raster_max_ns = nc->stats.s.raster_max_ns;
@@ -132,7 +132,7 @@ void notcurses_stats_reset(notcurses* nc, ncstats* stats){
     stash->writeout_ns += nc->stats.s.writeout_ns;
     stash->raster_ns += nc->stats.s.raster_ns;
     stash->render_ns += nc->stats.s.render_ns;
-    stash->render_bytes += nc->stats.s.render_bytes;
+    stash->raster_bytes += nc->stats.s.raster_bytes;
     stash->failed_renders += nc->stats.s.failed_renders;
     stash->failed_writeouts += nc->stats.s.failed_writeouts;
     stash->renders += nc->stats.s.renders;
@@ -199,11 +199,11 @@ void summarize_stats(notcurses* nc){
             totalbuf, minbuf, avgbuf, maxbuf);
   }
   if(stats->renders || stats->input_events){
-    ncbprefix(stats->render_bytes, 1, totalbuf, 1),
-    ncbprefix(stats->render_bytes ? stats->render_min_bytes : 0,
-            1, minbuf, 1),
-    ncbprefix(stats->renders ? stats->render_bytes / stats->renders : 0, 1, avgbuf, 1);
-    ncbprefix(stats->render_max_bytes, 1, maxbuf, 1),
+    ncbprefix(stats->raster_bytes, 1, totalbuf, 1),
+    ncbprefix(stats->raster_bytes ? stats->raster_min_bytes : 0,
+              1, minbuf, 1),
+    ncbprefix(stats->renders ? stats->raster_bytes / stats->renders : 0, 1, avgbuf, 1);
+    ncbprefix(stats->raster_max_bytes, 1, maxbuf, 1),
     fprintf(stderr, "%s%sB (%sB min, %sB avg, %sB max) %"PRIu64" input%s Ghpa: %"PRIu64 NL,
             clreol, totalbuf, minbuf, avgbuf, maxbuf,
             stats->input_events,
@@ -240,7 +240,7 @@ void summarize_stats(notcurses* nc){
           (stats->sprixelemissions + stats->sprixelelisions) == 0 ? 0 :
           (stats->sprixelelisions * 100.0) / (stats->sprixelemissions + stats->sprixelelisions),
           totalbuf,
-          stats->render_bytes ? (stats->sprixelbytes * 100.0) / stats->render_bytes : 0,
+          stats->raster_bytes ? (stats->sprixelbytes * 100.0) / stats->raster_bytes : 0,
           stats->appsync_updates,
           stats->writeouts ? stats->appsync_updates * 100.0 / stats->writeouts : 0);
 }
diff --git a/src/lib/termdesc.c b/src/lib/termdesc.c
index e26474bd89..36c5c7aa9a 100644
--- a/src/lib/termdesc.c
+++ b/src/lib/termdesc.c
@@ -832,8 +832,10 @@ int interrogate_terminfo(tinfo* ti, FILE* out, unsigned utf8,
     logpanic("failed opening Windows ConPTY\n");
     return -1;
   }
-  if(cursor_y && cursor_x){
-    locate_cursor(ti, cursor_y, cursor_x);
+  unsigned ucy, ucx;
+  if(locate_cursor(ti, &ucy, &ucx) == 0){
+    *cursor_y = ucy;
+    *cursor_x = ucx;
   }
 #elif defined(__linux__)
   ti->linux_fb_fd = -1;
diff --git a/src/lib/termdesc.h b/src/lib/termdesc.h
index 13f3849cb9..1186d40b1f 100644
--- a/src/lib/termdesc.h
+++ b/src/lib/termdesc.h
@@ -221,7 +221,7 @@ get_escape(const tinfo* tdesc, escape_e e){
   return NULL;
 }
 
-static inline int
+static inline uint16_t
 term_supported_styles(const tinfo* ti){
   return ti->supported_styles;
 }
diff --git a/src/lib/visual.c b/src/lib/visual.c
index 28f2544fe4..c0c644ec3e 100644
--- a/src/lib/visual.c
+++ b/src/lib/visual.c
@@ -70,11 +70,6 @@ ncplane* ncvisual_subtitle_plane(ncplane* parent, const ncvisual* ncv){
   return visual_implementation.visual_subtitle(parent, ncv);
 }
 
-char* ncvisual_subtitle(const ncvisual* ncv){
-  (void)ncv; // FIXME remove for abi3
-  return NULL;
-}
-
 int ncvisual_blit_internal(ncvisual* ncv, int rows, int cols, ncplane* n,
                            const struct blitset* bset, const blitterargs* barg){
   if(!(barg->flags & NCVISUAL_OPTION_NOINTERPOLATE)){
@@ -127,38 +122,6 @@ ncvisual_origin(const struct ncvisual_options* vopts, unsigned* restrict begy,
   *begx = vopts ? vopts->begx : 0;
 }
 
-
-int ncvisual_blitter_geom(const notcurses* nc, const ncvisual* n,
-                          const struct ncvisual_options* vopts,
-                          int* y, int* x, int* scaley, int* scalex,
-                          ncblitter_e* blitter){
-  ncvgeom geom;
-  const struct blitset* bset;
-  unsigned disppxy, disppxx, outy, outx;
-  int placey, placex;
-  if(ncvisual_geom_inner(&nc->tcache, n, vopts, &geom, &bset,
-                         &disppxy, &disppxx, &outy, &outx,
-                         &placey, &placex)){
-    return -1;
-  }
-  if(y){
-    *y = geom.pixy;
-  }
-  if(x){
-    *x = geom.pixx;
-  }
-  if(scaley){
-    *scaley = geom.scaley;
-  }
-  if(scalex){
-    *scalex = geom.scalex;
-  }
-  if(blitter){
-    *blitter = bset->geom;
-  }
-  return 0;
-}
-
 // create a plane in which to blit the sprixel. |disppixx| and |disppixy| are
 // scaled pixel geometry on output, and unused on input. |placey| and |placex|
 // are used to position the new plane, and reset to 0 on output. |outy| and
@@ -1175,23 +1138,6 @@ ncplane* ncvisual_blit(notcurses* nc, ncvisual* ncv, const struct ncvisual_optio
   return n;
 }
 
-// compatability wrapper around ncvisual_blit that provides the standard plane
-// plus NCVISUAL_OPTION_CHILDPLANE if vopts->n is NULL.
-ncplane* ncvisual_render(notcurses* nc, ncvisual* ncv, const struct ncvisual_options* vopts){
-  struct ncvisual_options fakevopts;
-  if(vopts == NULL){
-    memset(&fakevopts, 0, sizeof(fakevopts));
-  }else{
-    memcpy(&fakevopts, vopts, sizeof(fakevopts));
-  }
-  vopts = &fakevopts;
-  if(vopts->n == NULL){
-    fakevopts.n = notcurses_stdplane(nc);
-    fakevopts.flags |= NCVISUAL_OPTION_CHILDPLANE;
-  }
-  return ncvisual_blit(nc, ncv, vopts);
-}
-
 ncvisual* ncvisual_from_plane(const ncplane* n, ncblitter_e blit,
                               int begy, int begx,
                               unsigned leny, unsigned lenx){
@@ -1345,10 +1291,3 @@ bool notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))){
   }
   return visual_implementation.canopen_videos;
 }
-
-int ncvisual_inflate(ncvisual* n, int scale){
-  if(scale <= 0){
-    return -1;
-  }
-  return ncvisual_resize_noninterpolative(n, n->pixy * scale, n->pixx * scale);
-}
diff --git a/src/libcpp/NotCurses.cc b/src/libcpp/NotCurses.cc
index 05099583e7..c8c4777562 100644
--- a/src/libcpp/NotCurses.cc
+++ b/src/libcpp/NotCurses.cc
@@ -5,7 +5,6 @@ using namespace ncpp;
 
 notcurses_options NotCurses::default_notcurses_options = {
 	/* termtype */                 nullptr,
-	/* renderfp */                 nullptr,
 	/* loglevel */                 NCLogLevel::Silent,
 	/* margin_t */                 0,
 	/* margin_r */                 0,
diff --git a/src/poc/bitmapstates.c b/src/poc/bitmapstates.c
index 624e20bf8d..760ee7416b 100644
--- a/src/poc/bitmapstates.c
+++ b/src/poc/bitmapstates.c
@@ -12,8 +12,8 @@ emit(struct ncplane* n, const char* str){
 static int
 wipebitmap(struct notcurses* nc){
   unsigned cellpxy, cellpxx;
-  ncplane_pixelgeom(notcurses_stdplane(nc), NULL, NULL,
-                    &cellpxy, &cellpxx, NULL, NULL);
+  ncplane_pixel_geom(notcurses_stdplane(nc), NULL, NULL,
+                     &cellpxy, &cellpxx, NULL, NULL);
   int pixy = cellpxy * 6;
   int pixx = cellpxx * 6;
   uint32_t* pixels = malloc(sizeof(*pixels) * pixx * pixy);
diff --git a/src/poc/grid.c b/src/poc/grid.c
index fa0f283e43..7ea1c113c1 100644
--- a/src/poc/grid.c
+++ b/src/poc/grid.c
@@ -4,7 +4,7 @@ struct ncvisual*
 draw_grid(struct ncplane* stdn){
   unsigned maxby, maxbx;
   unsigned cellpxy, cellpxx;
-  ncplane_pixelgeom(stdn, NULL, NULL, &cellpxy, &cellpxx, &maxby, &maxbx);
+  ncplane_pixel_geom(stdn, NULL, NULL, &cellpxy, &cellpxx, &maxby, &maxbx);
   if(cellpxy <= 1 || cellpxx <= 1){
     fprintf(stderr, "cell-pixel geometry: %d %d\n", cellpxy, cellpxx);
     return NULL;
diff --git a/src/poc/interp.c b/src/poc/interp.c
index ca10fc06a9..eea0a38274 100644
--- a/src/poc/interp.c
+++ b/src/poc/interp.c
@@ -105,14 +105,14 @@ int main(void){
   }
   struct ncplane* stdn = notcurses_stdplane(nc);
   unsigned cellpixy, cellpixx;
-  ncplane_pixelgeom(stdn, NULL, NULL, &cellpixy, &cellpixx, NULL, NULL);
+  ncplane_pixel_geom(stdn, NULL, NULL, &cellpixy, &cellpixx, NULL, NULL);
   if(interp(nc, cellpixy, cellpixx)){
     goto err;
   }
   ncinput ni;
   do{
     notcurses_getc_blocking(nc, &ni);
-  }while(ni.evtype == NCTYPE_RELEASE);
+  }while(ni.id != (uint32_t)-1 && ni.evtype != NCTYPE_RELEASE);
   notcurses_stop(nc);
   return EXIT_SUCCESS;
 


Test patch:
diff --git a/src/tests/bitmap.cpp b/src/tests/bitmap.cpp
index 43514b5771..6ac8bce0b7 100644
--- a/src/tests/bitmap.cpp
+++ b/src/tests/bitmap.cpp
@@ -53,7 +53,7 @@ TEST_CASE("Bitmaps") {
     vopts.blitter = NCBLIT_PIXEL;
     vopts.flags = NCVISUAL_OPTION_NODEGRADE;
     unsigned maxy, maxx;
-    ncplane_pixelgeom(n_, nullptr, nullptr, nullptr, nullptr, &maxy, &maxx);
+    ncplane_pixel_geom(n_, nullptr, nullptr, nullptr, nullptr, &maxy, &maxx);
     CHECK(0 == ncvisual_resize(ncv, maxy, maxx));
     auto n = ncvisual_blit(nc_, ncv, &vopts);
     REQUIRE(nn == n);
@@ -306,8 +306,8 @@ TEST_CASE("Bitmaps") {
     vopts.x = 5;
     vopts.scaling = NCSCALE_NONE;
     auto ninf = ncvisual_blit(nc_, ncv, &vopts);
-    ncplane_set_name(ninf, "ninf");
     REQUIRE(nullptr != ninf);
+    ncplane_set_name(ninf, "ninf");
     // y of scaled might not be exactly equal to y of inflated since one
     // is based around what can be fit into a specific space, whereas the
     // other is based on a multiple of the original image size, which might
diff --git a/src/tests/metric.cpp b/src/tests/metric.cpp
index 954555e858..0c25cf95fd 100644
--- a/src/tests/metric.cpp
+++ b/src/tests/metric.cpp
@@ -10,7 +10,7 @@ char* impericize_ncmetric(uintmax_t val, uintmax_t decimal, char* buf,
   const char* decisep = localeconv()->decimal_point;
   REQUIRE(decisep);
   REQUIRE(1 ==  strlen(decisep));
-  REQUIRE(ncmetric(val, decimal, buf, omitdec, mult, uprefix));
+  REQUIRE(ncnmetric(val, INT_MAX, decimal, buf, omitdec, mult, uprefix));
   char* commie = buf;
   while( (commie = strstr(commie, decisep)) ){
     *commie = '.'; // https://dank.qemfd.net/images/16whcc.jpg
@@ -89,20 +89,20 @@ TEST_CASE("Metric") {
   SUBCASE("Maxints1024") {
     char buf[NCPREFIXSTRLEN + 1], gold[NCPREFIXSTRLEN + 1];
     // FIXME these will change based on the size of intmax_t and uintmax_t
-    REQUIRE(ncmetric(((double)(INTMAX_MAX - 1ull)), 1, buf, 0, 1024, 'i'));
+    REQUIRE(ncbprefix(((double)(INTMAX_MAX - 1ull)), 1, buf, 0));
     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - 1ull)) / (1ull << 60));
     CHECK(!strcmp(gold, buf));
-    REQUIRE(ncmetric(((double)(INTMAX_MAX - (1ull << 53))), 1, buf, 0, 1024, 'i'));
+    REQUIRE(ncbprefix(((double)(INTMAX_MAX - (1ull << 53))), 1, buf, 0));
     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - (1ull << 53))) / (1ull << 60));
     CHECK(!strcmp(gold, buf));
-    REQUIRE(ncmetric(INTMAX_MAX + 1ull, 1, buf, 0, 1024, 'i'));
+    REQUIRE(ncbprefix(INTMAX_MAX + 1ull, 1, buf, 0));
     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX + 1ull)) / (1ull << 60));
     CHECK(!strcmp(gold, buf));
     impericize_ncmetric(UINTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
     CHECK(!strcmp("16.00Ei", buf));
     impericize_ncmetric(UINTMAX_MAX, 1, buf, 0, 1024, 'i');
     CHECK(!strcmp("16.00Ei", buf));
-    ncmetric(UINTMAX_MAX - (1ull << 53), 1, buf, 0, 1024, 'i');
+    ncbprefix(UINTMAX_MAX - (1ull << 53), 1, buf, 0);
     sprintf(gold, "%.2fEi", ((double)UINTMAX_MAX - (1ull << 53)) / (1ull << 60));
     CHECK(!strcmp(gold, buf));
   }
@@ -116,7 +116,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 0, 1000, '\0');
+      ncqprefix(val, 1, buf, 0);
       const int sidx = i / 3;
       snprintf(gold, sizeof(gold), "%ju%s00%c", goldval, decisep, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -139,7 +139,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 1, 1000, '\0');
+      ncqprefix(val, 1, buf, 1);
       const int sidx = i / 3;
       snprintf(gold, sizeof(gold), "%ju%c", goldval, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -162,7 +162,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 0, 1024, 'i');
+      ncbprefix(val, 1, buf, 0);
       const int sidx = i / 10;
       snprintf(gold, sizeof(gold), "%ju%s00%ci", goldval, decisep, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -185,7 +185,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 1, 1024, 'i');
+      ncbprefix(val, 1, buf, 1);
       const int sidx = i / 10;
       snprintf(gold, sizeof(gold), "%ju%ci", goldval, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -208,7 +208,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 0, 1000, '\0');
+      ncqprefix(val, 1, buf, 0);
       const int sidx = i / 10;
       snprintf(gold, sizeof(gold), "%.2f%c", ((double)val) / vfloor, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -231,7 +231,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val, 1, buf, 0, 1024, 'i');
+      ncbprefix(val, 1, buf, 0);
       const int sidx = i ? (i - 1) / 3 : 0;
       snprintf(gold, sizeof(gold), "%.2f%ci", ((double)val) / vfloor, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -254,7 +254,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val - 1, 1, buf, 0, 1000, '\0');
+      ncqprefix(val - 1, 1, buf, 0);
       const int sidx = i ? (i - 1) / 3 : 0;
       snprintf(gold, sizeof(gold), "%.2f%c", ((double)(val - 1)) / vfloor, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -277,7 +277,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val + 1, 1, buf, 0, 1000, '\0');
+      ncqprefix(val + 1, 1, buf, 0);
       const int sidx = i / 3;
       snprintf(gold, sizeof(gold), "%.2f%c", ((double)(val + 1)) / vfloor, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
@@ -300,7 +300,7 @@ TEST_CASE("Metric") {
     uintmax_t val = 1;
     size_t i = 0;
     do{
-      ncmetric(val - 1, 1, buf, 0, 1024, 'i');
+      ncbprefix(val - 1, 1, buf, 0);
       const int sidx = i ? (i - 1) / 3 : 0;
       snprintf(gold, sizeof(gold), "%.2f%ci", ((double)(val - 1)) / vfloor, suffixes[sidx]);
       CHECK(!strcmp(gold, buf));
diff --git a/src/tests/plane.cpp b/src/tests/plane.cpp
index 5a918d848f..7d0a707dd0 100644
--- a/src/tests/plane.cpp
+++ b/src/tests/plane.cpp
@@ -1051,22 +1051,22 @@ TEST_CASE("Plane") {
     CHECK(x == 2);
     CHECK(1 == ncplane_set_base(n, " ", 0, channels));
     CHECK(0 == notcurses_render(nc_));
-    CHECK(0 == ncplane_moverel(n, -1, 0)); // up
+    CHECK(0 == ncplane_move_rel(n, -1, 0)); // up
     ncplane_yx(n, &y, &x);
     CHECK(y == 1);
     CHECK(x == 2);
     CHECK(0 == notcurses_render(nc_));
-    CHECK(0 == ncplane_moverel(n, 2, 0)); // down
+    CHECK(0 == ncplane_move_rel(n, 2, 0)); // down
     ncplane_yx(n, &y, &x);
     CHECK(y == 3);
     CHECK(x == 2);
     CHECK(0 == notcurses_render(nc_));
-    CHECK(0 == ncplane_moverel(n, 0, -1)); // left
+    CHECK(0 == ncplane_move_rel(n, 0, -1)); // left
     ncplane_yx(n, &y, &x);
     CHECK(y == 3);
     CHECK(x == 1);
     CHECK(0 == notcurses_render(nc_));
-    CHECK(0 == ncplane_moverel(n, 0, 2)); // right
+    CHECK(0 == ncplane_move_rel(n, 0, 2)); // right
     ncplane_yx(n, &y, &x);
     CHECK(y == 3);
     CHECK(x == 3);