diff --git a/doc/man/index.html b/doc/man/index.html
index 9fe36c94e..f0ab0b2d1 100644
--- a/doc/man/index.html
+++ b/doc/man/index.html
@@ -37,8 +37,8 @@
notcurses-tester—unit test driver
notcurses-tetris—Tetris in a terminal
notcurses-view—renders images and video to a terminal
-
C library (section 3)
+
C library (section 3)
notcurses_capabilities—runtime capability detection
notcurses_cell—operations on
cell objects
notcurses_channels—operations on the
channel type
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index a52c0db1e..c54474d9a 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -13,12 +13,14 @@
#include
#include
#include
+// take host byte order and turn it into network (reverse on LE, no-op on BE),
+// then reverse that, guaranteeing LE. htole(x) == ltohe(x).
#ifdef __linux__
#include
-#define ntole(x) (bswap_32(htonl(x)))
+#define htole(x) (bswap_32(htonl(x)))
#else
#include
-#define ntole(x) (bswap32(htonl(x)))
+#define htole(x) (bswap32(htonl(x)))
#endif
#include
#include
@@ -576,8 +578,8 @@ typedef struct cell {
} cell;
#define CELL_TRIVIAL_INITIALIZER { }
-#define CELL_CHAR_INITIALIZER(c) { .gcluster = (ntole(c)), .gcluster_backstop = 0, .reserved = 0, .stylemask = 0, .channels = 0, }
-#define CELL_INITIALIZER(c, s, chan) { .gcluster = (ntole(c)), .gcluster_backstop = 0, .reserved = 0, .stylemask = (s), .channels = (chan), }
+#define CELL_CHAR_INITIALIZER(c) { .gcluster = (htole(c)), .gcluster_backstop = 0, .reserved = 0, .stylemask = 0, .channels = 0, }
+#define CELL_INITIALIZER(c, s, chan) { .gcluster = (htole(c)), .gcluster_backstop = 0, .reserved = 0, .stylemask = (s), .channels = (chan), }
static inline void
cell_init(cell* c){
@@ -729,7 +731,7 @@ static inline int
cell_load_char(struct ncplane* n, cell* c, char ch){
cell_release(n, c);
c->channels &= ~(CELL_WIDEASIAN_MASK | CELL_NOBACKGROUND_MASK);
- c->gcluster = ntole((uint32_t)ch);
+ c->gcluster = htole((uint32_t)ch);
return 1;
}
@@ -2417,83 +2419,94 @@ API int ncblit_bgrx(const void* data, int linesize,
// The ncpixel API facilitates direct management of the pixels within an
// ncvisual (ncvisuals keep a backing store of 32-bit RGBA pixels, and render
// them down to terminal graphics in ncvisual_render()).
-
-// Extract the 8-bit alpha component from a pixel
-static inline uint32_t
-ncpixel(int r, int g, int b){
- if(r < 0) r = 0;
- if(r > 255) r = 255;
- if(g < 0) g = 0;
- if(g > 255) g = 255;
- if(b < 0) b = 0;
- if(b > 255) b = 255;
- return 0xff000000ul | r | (b << 8u) | (g << 16u);
-}
+//
+// Per libav, we "store as BGRA on little-endian, and ARGB on big-endian".
+// This is an RGBA *byte-order* scheme. libav emits bytes, not words. Those
+// bytes are R-G-B-A. When read as words, on little endian this will be ABGR,
+// and on big-endian this will be RGBA. force everything to LE ABGR, a no-op on
+// and thus favoring little-endian. Take that, big-endian mafia!
// Extract the 8-bit alpha component from a pixel
static inline unsigned
ncpixel_a(uint32_t pixel){
- return (pixel & 0xff000000ul) >> 24u;
+ return (htole(pixel) & 0xff000000ul) >> 24u;
}
-// Extract the 8-bit red component from a pixel
+// Extract the 8-bit red component from an ABGR pixel
static inline unsigned
ncpixel_r(uint32_t pixel){
- return (pixel & 0x000000fful);
+ return (htole(pixel) & 0x000000fful);
}
-// Extract the 8-bit green component from a pixel
+// Extract the 8-bit green component from an ABGR pixel
static inline unsigned
ncpixel_g(uint32_t pixel){
- return (pixel & 0x0000ff00ul) >> 8u;
+ return (htole(pixel) & 0x0000ff00ul) >> 8u;
}
-// Extract the 8-bit blue component from a pixel
+// Extract the 8-bit blue component from an ABGR pixel
static inline unsigned
ncpixel_b(uint32_t pixel){
- return (pixel & 0x00ff0000ul) >> 16u;
+ return (htole(pixel) & 0x00ff0000ul) >> 16u;
}
-// Set the 8-bit alpha component of a pixel
+// Set the 8-bit alpha component of an ABGR pixel
static inline int
ncpixel_set_a(uint32_t* pixel, int a){
if(a > 255 || a < 0){
return -1;
}
- *pixel = (*pixel & 0x00fffffful) | (a << 24u);
+ *pixel = htole((htole(*pixel) & 0x00fffffful) | (a << 24u));
return 0;
}
-// Set the 8-bit red component of a pixel
+// Set the 8-bit red component of an ABGR pixel
static inline int
ncpixel_set_r(uint32_t* pixel, int r){
if(r > 255 || r < 0){
return -1;
}
- *pixel = (*pixel & 0xffffff00ul) | r;
+ *pixel = htole((htole(*pixel) & 0xffffff00ul) | r);
return 0;
}
-// Set the 8-bit green component of a pixel
+// Set the 8-bit green component of an ABGR pixel
static inline int
ncpixel_set_g(uint32_t* pixel, int g){
if(g > 255 || g < 0){
return -1;
}
- *pixel = (*pixel & 0xffff00fful) | (g << 8u);
+ *pixel = htole((htole(*pixel) & 0xffff00fful) | (g << 8u));
return 0;
}
-// Set the 8-bit blue component of a pixel
+// Set the 8-bit blue component of an ABGR pixel
static inline int
ncpixel_set_b(uint32_t* pixel, int b){
if(b > 255 || b < 0){
return -1;
}
- *pixel = (*pixel & 0xff00fffful) | (b << 16u);
+ *pixel = htole((htole(*pixel) & 0xff00fffful) | (b << 16u));
return 0;
}
+// Construct a libav-compatible ABGR pixel, clipping at [0, 255).
+static inline uint32_t
+ncpixel(int r, int g, int b){
+ uint32_t pixel = 0;
+ ncpixel_set_a(&pixel, 0xff);
+ if(r < 0) r = 0;
+ if(r > 255) r = 255;
+ ncpixel_set_r(&pixel, r);
+ if(g < 0) g = 0;
+ if(g > 255) g = 255;
+ ncpixel_set_g(&pixel, g);
+ if(b < 0) b = 0;
+ if(b > 255) b = 255;
+ ncpixel_set_b(&pixel, b);
+ return pixel;
+}
+
// set the RGB values of an RGB pixel
static inline int
ncpixel_set_rgb8(uint32_t* pixel, int r, int g, int b){
diff --git a/src/input/input.cpp b/src/input/input.cpp
index 688c80c90..cb01c8f59 100644
--- a/src/input/input.cpp
+++ b/src/input/input.cpp
@@ -213,7 +213,7 @@ int input_demo(ncpp::NotCurses* nc) {
popts.minchannels = popts.maxchannels = 0;
channels_set_fg_rgb8(&popts.minchannels, 0x40, 0x50, 0xb0);
channels_set_fg_rgb8(&popts.maxchannels, 0x40, 0xff, 0xd0);
- popts.gridtype = static_cast(NCBLIT_2x2);
+ popts.gridtype = static_cast(NCBLIT_3x2);
plot = ncuplot_create(pplane, &popts, 0, 0);
if(!plot){
return EXIT_FAILURE;
diff --git a/src/lib/egcpool.h b/src/lib/egcpool.h
index 5af615da2..c4e222967 100644
--- a/src/lib/egcpool.h
+++ b/src/lib/egcpool.h
@@ -252,13 +252,13 @@ egcpool_dump(egcpool* pool){
// unsafe results if called on a simple cell.
static inline uint32_t
cell_egc_idx(const cell* c){
- return ntole(c->gcluster) & 0x00fffffflu;
+ return htole(c->gcluster) & 0x00fffffflu;
}
// Is the cell simple (a UTF8-encoded EGC of four bytes or fewer)?
static inline bool
cell_simple_p(const cell* c){
- return (ntole(c->gcluster) & 0xff000000ul) != 0x01000000ul;
+ return (htole(c->gcluster) & 0xff000000ul) != 0x01000000ul;
}
// only applies to complex cells, do not use on simple cells
diff --git a/src/lib/internal.h b/src/lib/internal.h
index a23c73916..5e893c27a 100644
--- a/src/lib/internal.h
+++ b/src/lib/internal.h
@@ -639,7 +639,7 @@ pool_release(egcpool* pool, cell* c){
// set the cell 'c' to point into the egcpool at location 'eoffset'
static inline void
set_gcluster_egc(cell* c, int eoffset){
- c->gcluster = ntole(0x01000000ul) + eoffset;
+ c->gcluster = htole(0x01000000ul) + htole(eoffset);
}
// Duplicate one cell onto another, possibly crossing ncplanes.
@@ -969,15 +969,13 @@ pool_blit_direct(egcpool* pool, cell* c, const char* gcluster, int bytes, int co
}
if(bytes <= 1){
assert(cols < 2);
- c->gcluster = 0;
- ((unsigned char*)&c->gcluster)[0] = *gcluster;
+ c->gcluster = htole((uint32_t)*gcluster);
return bytes;
}
if(bytes <= 4){
- if(strcmp(gcluster, (const char*)&c->gcluster)){
- c->gcluster = 0;
- memcpy(&c->gcluster, gcluster, bytes);
- }
+ c->gcluster = 0;
+ memcpy(&c->gcluster, gcluster, bytes);
+ c->gcluster = htole(c->gcluster);
return bytes;
}
int eoffset = egcpool_stash(pool, gcluster, bytes);
diff --git a/tests/cell.cpp b/tests/cell.cpp
index 733b57e82..9617ee148 100644
--- a/tests/cell.cpp
+++ b/tests/cell.cpp
@@ -2,13 +2,14 @@
#include "egcpool.h"
TEST_CASE("Cell") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(nullptr != n_);
diff --git a/tests/egcpool.cpp b/tests/egcpool.cpp
index 4be9234e6..16d22ab95 100644
--- a/tests/egcpool.cpp
+++ b/tests/egcpool.cpp
@@ -12,9 +12,15 @@ TEST_CASE("EGCpool") {
CHECK(!pool_.poolused);
}
- if(!enforce_utf8()){
+ auto nc_ = testing_notcurses();
+ if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
+ CHECK(0 == notcurses_stop(nc_));
SUBCASE("UTF8EGC") {
const char* wstr = "☢";
diff --git a/tests/fills.cpp b/tests/fills.cpp
index aa7e8ccfa..6b11d730d 100644
--- a/tests/fills.cpp
+++ b/tests/fills.cpp
@@ -3,13 +3,14 @@
#include "main.h"
TEST_CASE("Fills") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
diff --git a/tests/layout.cpp b/tests/layout.cpp
index 11f2ed15d..09eb9fc1c 100644
--- a/tests/layout.cpp
+++ b/tests/layout.cpp
@@ -189,7 +189,7 @@ TEST_CASE("TextLayout") {
// lay out text where a wide word crosses the boundary
SUBCASE("LayoutCrossBoundaryWide") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
struct ncplane_options nopts = {
.y = 0,
.x = 0,
@@ -245,7 +245,7 @@ TEST_CASE("TextLayout") {
// a long word (one requiring a split no matter what) ought not force the
// next line, but instead be printed where it starts
SUBCASE("LayoutTransPlanarWide") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
struct ncplane_options nopts = {
.y = 0,
.x = 0,
@@ -428,7 +428,7 @@ TEST_CASE("TextLayout") {
// create a plane of three rows, and exactly fill two with wide chars
SUBCASE("LayoutFillsPlaneWide") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
struct ncplane_options nopts = {
.y = 0,
.x = 0,
diff --git a/tests/main.cpp b/tests/main.cpp
index 67cc14e30..34747c6c3 100644
--- a/tests/main.cpp
+++ b/tests/main.cpp
@@ -28,17 +28,6 @@ auto find_data(const char* datum) -> char* {
return strdup(p.c_str());
}
-auto enforce_utf8() -> bool {
- char* enc = nl_langinfo(CODESET);
- if(!enc){
- return false;
- }
- if(strcmp(enc, "UTF-8")){
- return false;
- }
- return true;
-}
-
static void
handle_opts(const char** argv){
bool inarg = false;
diff --git a/tests/main.h b/tests/main.h
index f23da5e24..1e1af41a6 100644
--- a/tests/main.h
+++ b/tests/main.h
@@ -11,7 +11,6 @@
#include "internal.h"
auto find_data(const char* datum) -> char*;
-auto enforce_utf8() -> bool;
auto testing_notcurses() -> struct notcurses*;
auto ncreel_validate(const ncreel* n) -> bool;
diff --git a/tests/metric.cpp b/tests/metric.cpp
index 340da4917..bf4181e73 100644
--- a/tests/metric.cpp
+++ b/tests/metric.cpp
@@ -421,8 +421,17 @@ TEST_CASE("Metric") {
}
SUBCASE("NegativePowersOfTen") {
+ auto nc_ = testing_notcurses();
+ if(!nc_){
+ return;
+ }
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
+ CHECK(0 == notcurses_stop(nc_));
const wchar_t* smallsuffixes;
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
smallsuffixes = L"yzafpnµm";
}else{
smallsuffixes = L"yzafpnum";
diff --git a/tests/ncplane.cpp b/tests/ncplane.cpp
index cdb88858a..ae8f3008d 100644
--- a/tests/ncplane.cpp
+++ b/tests/ncplane.cpp
@@ -18,13 +18,14 @@ void BoxPermutationsRounded(struct notcurses* nc, struct ncplane* n, unsigned ed
}
TEST_CASE("NCPlane") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
diff --git a/tests/plot.cpp b/tests/plot.cpp
index 7f8050be3..0eb585d73 100644
--- a/tests/plot.cpp
+++ b/tests/plot.cpp
@@ -3,13 +3,14 @@
#include
TEST_CASE("Plot") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
auto n_ = notcurses_stdplane(nc_);
REQUIRE(n_);
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
diff --git a/tests/reader.cpp b/tests/reader.cpp
index 25d620a1a..cf389cd3f 100644
--- a/tests/reader.cpp
+++ b/tests/reader.cpp
@@ -26,7 +26,8 @@ TEST_CASE("Readers") {
};
auto ncp = ncplane_create(notcurses_stdplane(nc_), &nopts);
uint64_t echannels = CHANNELS_RGB_INITIALIZER(0xff, 0x44, 0xff, 0, 0, 0);
- ncplane_set_base(ncp, enforce_utf8() ? strdup("▒") : strdup("x"), 0, echannels);
+ ncplane_set_base(ncp, notcurses_canutf8(nc_) ? strdup("▒") : strdup("x"),
+ 0, echannels);
auto nr = ncreader_create(ncp, &opts);
REQUIRE(nullptr != nr);
CHECK(0 == notcurses_render(nc_));
diff --git a/tests/rotate.cpp b/tests/rotate.cpp
index 098aed5bb..41ac2c814 100644
--- a/tests/rotate.cpp
+++ b/tests/rotate.cpp
@@ -27,13 +27,14 @@ void RotateCCW(struct notcurses* nc, struct ncplane* n) {
}
TEST_CASE("Rotate") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
if(!nc_){
return;
}
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
int dimy, dimx;
struct ncplane* n_ = notcurses_stddim_yx(nc_, &dimy, &dimx);
REQUIRE(n_);
diff --git a/tests/visual.cpp b/tests/visual.cpp
index c1f9990a8..3c3175ba8 100644
--- a/tests/visual.cpp
+++ b/tests/visual.cpp
@@ -272,7 +272,7 @@ TEST_CASE("Visual") {
// write a checkerboard pattern and verify the NCBLIT_2x1 output
SUBCASE("Dualblitter") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
constexpr int DIMY = 10;
constexpr int DIMX = 11; // odd number to get checkerboard effect
auto rgba = new uint32_t[DIMY * DIMX];
@@ -312,7 +312,7 @@ TEST_CASE("Visual") {
// write a checkerboard pattern and verify the NCBLIT_2x2 output
SUBCASE("Quadblitter") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
constexpr int DIMY = 10;
constexpr int DIMX = 11; // odd number to get checkerboard effect
auto rgba = new uint32_t[DIMY * DIMX];
@@ -352,7 +352,7 @@ TEST_CASE("Visual") {
// close-in verification of each quadblitter output EGC
SUBCASE("QuadblitterEGCs") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
// there are 16 configurations, each mapping four (2x2) pixels
int DIMX = 32;
int DIMY = 2;
@@ -426,7 +426,7 @@ TEST_CASE("Visual") {
// quadblitter with all 4 colors equal ought generate space
SUBCASE("Quadblitter4Same") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
const uint32_t pixels[4] = { 0xff605040, 0xff605040, 0xff605040, 0xff605040 };
auto ncv = ncvisual_from_rgba(pixels, 2, 2 * sizeof(*pixels), 2);
REQUIRE(nullptr != ncv);
@@ -462,7 +462,7 @@ TEST_CASE("Visual") {
// quadblitter with three pixels equal ought generate three-quarter block
SUBCASE("Quadblitter3Same") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
const uint32_t pixels[4][4] = {
{ 0xffcccccc, 0xff605040, 0xff605040, 0xff605040 },
{ 0xff605040, 0xffcccccc, 0xff605040, 0xff605040 },
@@ -505,7 +505,7 @@ TEST_CASE("Visual") {
// quadblitter with two sets of two equal pixels
SUBCASE("Quadblitter2Pairs") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
const uint32_t pixels[6][4] = {
{ 0xffcccccc, 0xffcccccc, 0xff605040, 0xff605040 },
{ 0xffcccccc, 0xff605040, 0xffcccccc, 0xff605040 },
@@ -555,7 +555,7 @@ TEST_CASE("Visual") {
// quadblitter with one pair plus two split
SUBCASE("Quadblitter1Pair") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
const uint32_t pixels[6][4] = {
{ 0xffcccccc, 0xff444444, 0xff605040, 0xff605040 },
{ 0xff444444, 0xff605040, 0xffcccccc, 0xff605040 },
@@ -605,7 +605,7 @@ TEST_CASE("Visual") {
// quadblitter with one pair plus two split
SUBCASE("QuadblitterAllDifferent") {
- if(enforce_utf8()){
+ if(notcurses_canutf8(nc_)){
const uint32_t pixels[6][4] = {
{ 0xffdddddd, 0xff000000, 0xff111111, 0xff222222 },
{ 0xff000000, 0xff111111, 0xffdddddd, 0xff222222 },
diff --git a/tests/wide.cpp b/tests/wide.cpp
index 6d99e3567..58bfb7b1a 100644
--- a/tests/wide.cpp
+++ b/tests/wide.cpp
@@ -8,11 +8,12 @@ const char SCORPION[] = "\xf0\x9f\xa6\x82"; // U+1F982 SCORPION
const char FROG[] = "\xf0\x9f\x90\xb8"; // U+1F438 FROG FACE
TEST_CASE("Wide") {
- if(!enforce_utf8()){
- return;
- }
auto nc_ = testing_notcurses();
REQUIRE(nullptr != nc_);
+ if(!notcurses_canutf8(nc_)){
+ CHECK(0 == notcurses_stop(nc_));
+ return;
+ }
struct ncplane* n_ = notcurses_stdplane(nc_);
REQUIRE(n_);