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_);