diff --git a/.drone.yml b/.drone.yml index 38c9594af..4f03effa8 100644 --- a/.drone.yml +++ b/.drone.yml @@ -25,22 +25,22 @@ steps: - export LANG=en_US.UTF-8 - mkdir build - cd build - - cmake .. -DCMAKE_BUILD_TYPE=Release + - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=none .. + - make + - ./notcurses-tester -p ../data +--- +kind: pipeline +type: docker +name: fedora-rawhide + +steps: +- name: fedora-rawhide + image: dankamongmen/fedora33:2020-06-08a + commands: + - export LANG=en_US.UTF-8 + - dnf install -y doctest-devel + - mkdir build + - cd build + - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=oiio .. - make - ./notcurses-tester -p ../data -#--- -#kind: pipeline -#type: docker -#name: fedora-rawhide -# -#steps: -#- name: fedora-rawhide -# image: dankamongmen/fedora33:2020-05-29a -# commands: -# - export LANG=en_US.UTF-8 -# - mkdir build -# - cd build -# - ls -# - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_MULTIMEDIA=oiio .. -# - make -# - ./notcurses-tester -p ../data diff --git a/NEWS.md b/NEWS.md index 8c34fb9ef..1f009a5fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -6,6 +6,9 @@ rearrangements of Notcurses. that `struct`'s `flags` field. Each `bool` has its own `NCOPTION_`. * Added a Pixel API for working directly with the contents of `ncvisual`s, including `ncvisual_at_yx()` and `ncvisual_set_yx()`. + * Added `ncplane_puttext()` for writing multiline, line-broken text. + * Added `ncplane_putnstr()`, `ncplane_putnstr_yx()`, and + `ncplane_putnstr_aligned()` for byte-limited output of UTF-8. * 1.4.5 (2020-06-04) * `ncblit_rgba()` and `ncblit_bgrx()` have replaced most of their arguments diff --git a/USAGE.md b/USAGE.md index 68251a693..4716aaaee 100644 --- a/USAGE.md +++ b/USAGE.md @@ -902,12 +902,11 @@ int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* sby // 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 -// of ncplane_putc()). Advances the cursor by some positive number of cells +// of ncplane_putc()). Advances the cursor by some positive number of columns // (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 cells +// On error, a non-positive number is returned, indicating the number of columns // which were written before the error. -static inline int -ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters); +int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters); static inline int ncplane_putstr(struct ncplane* n, const char* gclustarr){ @@ -935,6 +934,22 @@ ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr){ return ret; } +// 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 +// of ncplane_putc()). Advances the cursor by some positive number of columns +// (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. +int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters); + +static inline int +ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr){ + return ncplane_putnstr_yx(n, -1, -1, s, gclustarr); +} + +int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, + size_t s, const char* s); + static inline int ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align, const wchar_t* gclustarr){ @@ -1001,6 +1016,24 @@ ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* fo } ``` +Multiline chunks of human-readable text can be written with +`ncplane_puttext()` even if the plane does not have scrolling enabled. Such +text will be broken up across lines using the Unicode line-breaking algorithm +of [Unicode Annex #14](http://www.unicode.org/reports/tr14/tr14-34.html). + +```c +// Write the specified text to the plane, breaking lines sensibly, beginning at +// the specified line. Returns the number of columns written. When breaking a +// line, the line will be cleared to the end of the plane (the last line will +// *not* be so cleared). The number of bytes written from the input is written +// to '*bytes' if it is not NULL. Cleared columns are included in the return +// value, but *not* included in the number of bytes written. Leaves the cursor +// at the end of output. A partial write will be accomplished as far as it can; +// determine whether the write completed by inspecting '*bytes'. +int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, + const char* text, size_t* bytes); +``` + Lines and boxes can be drawn, interpolating their colors between their two endpoints. For a line of a single color, be sure to specify the same channels on both sides. Boxes allow fairly detailed specification of how they're drawn. diff --git a/doc/man/man3/notcurses_output.3.md b/doc/man/man3/notcurses_output.3.md index a46c06c16..53d0e72df 100644 --- a/doc/man/man3/notcurses_output.3.md +++ b/doc/man/man3/notcurses_output.3.md @@ -10,93 +10,83 @@ notcurses_output - output to ncplanes **#include ** -**static inline int -ncplane_putc(struct ncplane* n, const cell* c);** +**static inline int ncplane_putc(struct ncplane* n, const cell* c);** **int ncplane_putc_yx(struct ncplane* n, int y, int x, const cell* c);** -**static inline int -ncplane_putsimple(struct ncplane* n, char c);** +**static inline int ncplane_putsimple(struct ncplane* n, char c);** -**static inline int -ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c);** +**static inline int ncplane_putsimple_yx(struct ncplane* n, int y, int x, char c);** **int ncplane_putsimple_stainable(struct ncplane* n, char c);** -**static inline int -ncplane_putwc(struct ncplane* n, wchar_t w);** +**static inline int ncplane_putwc(struct ncplane* n, wchar_t w);** **int ncplane_putwc_yx(struct ncplane* n, int y, int x, wchar_t w);** -**static inline int -ncplane_putegc(struct ncplane* n, const char* gclust, int* sbytes);** +**static inline int ncplane_putegc(struct ncplane* n, const char* gclust, int* sbytes);** **int ncplane_putegc_yx(struct ncplane* n, int y, int x, const char* gclust, int* sbytes);** **int ncplane_putegc_stainable(struct ncplane* n, const char* gclust, int* sbytes);** -**static inline int -ncplane_putwegc(struct ncplane* n, const wchar_t* gclust, int* sbytes);** +**static inline int ncplane_putwegc(struct ncplane* n, const wchar_t* gclust, int* sbytes);** -**static inline int -ncplane_putwegc_yx(struct ncplane* n, int y, int x, const wchar_t* gclust, int* sbytes);** +**static inline int ncplane_putwegc_yx(struct ncplane* n, int y, int x, const wchar_t* gclust, int* sbytes);** **int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* sbytes);** -**static inline int -ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclustarr);** +**int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclustarr);** -**static inline int -ncplane_putstr(struct ncplane* n, const char* gclustarr);** +**static inline int ncplane_putstr(struct ncplane* n, const char* gclustarr);** -**int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align, - const char* s);** +**int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align, const char* s);** **int ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr);** -**static inline int -ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align, - const wchar_t* gclustarr);** +**static inline int ncplane_putwstr_aligned(struct ncplane* n, int y, ncalign_e align, const wchar_t* gclustarr);** -**static inline int -ncplane_putwstr(struct ncplane* n, const wchar_t* gclustarr);** +**static inline int ncplane_putwstr(struct ncplane* n, const wchar_t* gclustarr);** -**int ncplane_vprintf_aligned(struct ncplane* n, int y, ncalign_e align, - const char* format, va_list ap);** +**int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters);** -**int ncplane_vprintf_yx(struct ncplane* n, int y, int x, - const char* format, va_list ap);** +**static inline int ncplane_putnstr(struct ncplane* n, size_t s, const char* gclustarr);** -**static inline int -ncplane_vprintf(struct ncplane* n, const char* format, va_list ap);** +**int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* s);** -**static inline int -ncplane_printf(struct ncplane* n, const char* format, ...);** +**int ncplane_vprintf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, va_list ap);** -**static inline int -ncplane_printf_yx(struct ncplane* n, int y, int x, const char* format, ...);** +**int ncplane_vprintf_yx(struct ncplane* n, int y, int x, const char* format, va_list ap);** -**static inline int -ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, ...);** +**static inline int ncplane_vprintf(struct ncplane* n, const char* format, va_list ap);** + +**static inline int ncplane_printf(struct ncplane* n, const char* format, ...);** + +**static inline int ncplane_printf_yx(struct ncplane* n, int y, int x, const char* format, ...);** + +**static inline int ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* format, ...);** **int ncplane_cursor_move_yx(struct ncplane* n, int y, int x);** +**int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes);** + # DESCRIPTION These functions write EGCs (Extended Grapheme Clusters) to the specified **struct ncplane**s. The following inputs are supported: -* **ncplane_putc(3)**: writes a single cell (see **notcurses_cell(3)**) -* **ncplane_put_simple(3)**: writes a single 7-bit ASCII character -* **ncplane_putwc(3)**: writes a single **wchar_t** (following UTF-8 conversion) -* **ncplane_putwegc(3)**: writes a single EGC from an array of **wchar_t** -* **ncplane_putegc(3)**: writes a single EGC from an array of UTF-8 -* **ncplane_putstr(3)**: writes a set of EGCs from an array of UTF-8 -* **ncplane_putwstr(3)**: writes a set of EGCs from an array of **wchar_t** -* **ncplane_vprintf(3)**: formatted output using **va_list** -* **ncplane_printf(3)**: formatted output using variadic arguments +* **ncplane_putc()**: writes a single cell (see **notcurses_cell(3)**) +* **ncplane_put_simple()**: writes a single 7-bit ASCII character +* **ncplane_putwc()**: writes a single **wchar_t** (following UTF-8 conversion) +* **ncplane_putwegc()**: writes a single EGC from an array of **wchar_t** +* **ncplane_putegc()**: writes a single EGC from an array of UTF-8 +* **ncplane_putstr()**: writes a set of EGCs from an array of UTF-8 +* **ncplane_putwstr()**: writes a set of EGCs from an array of **wchar_t** +* **ncplane_vprintf()**: formatted output using **va_list** +* **ncplane_printf()**: formatted output using variadic arguments +* **ncplane_puttext()**: multi-line, line-broken, aligned text -All of these use the **ncplane**'s active styling, save **notcurses_putc(3)**, +All of these use the **ncplane**'s active styling, save **notcurses_putc()**, which uses the cell's styling. Functions accepting a single EGC expect a series of **wchar_t** terminated by **L'\0'** or a series of UTF-8 **char** terminated by **'\0'**. The EGC must be well-formed, and must not contain any cluster @@ -104,10 +94,10 @@ breaks. For more information, consult [UnicodeĀ® Standard Annex #29](https://uni Functions accepting a set of EGCs must consist of a series of well-formed EGCs, broken by cluster breaks, terminated by the appropriate NUL terminator. -These functions output to the `ncplane`'s current cursor location. They *do not* -move to the next line upon reaching the right extreme of the containing plane. -If the entirety of the content cannot be output, they will output as much as -possible. +These functions output to the `ncplane`'s current cursor location. Aside from +**ncplane_puttext()**, they *do not* move to the next line upon reaching the +right extreme of the containing plane. If the entirety of the content cannot be +output, they will output as much as possible. Each of these base functions has two additional forms: diff --git a/doc/release-checklist.md b/doc/release-checklist.md index cc51acb73..a5a74e4e4 100644 --- a/doc/release-checklist.md +++ b/doc/release-checklist.md @@ -1,3 +1,4 @@ +* Review the testing checklist (doc/testing-checklist.md) * clang-tidy check with something like: * `cmake "-DCMAKE_CXX_CLANG_TIDY=/usr/bin/clang-tidy-11;-checks=-*,clang-analyzer-*,modernize-*,performance-*" ..` * `scan-build cmake .. && scan-build make` diff --git a/doc/testing-checklist.md b/doc/testing-checklist.md new file mode 100644 index 000000000..c3e34c641 --- /dev/null +++ b/doc/testing-checklist.md @@ -0,0 +1,22 @@ +# Release testing + +## Unit tests + +Run unit tests (`make && make test`): +* In each multimedia configuration (`ffmpeg`, `oiio`, `none`) +* With LANG set to `fr_FR.UTF-8` (to test comma as decimal separator) +* All must pass + +## Manual tests + +Run, using `valgrind --tool=memcheck --leak-check=full`: +* `notcurses-demo` in each of the three multimedia configurations +* `notcurses-demo` with `USE_QRCODEGEN=off` +* `notcurses-ncreel` +* `notcurses-input` +* `notcurses-view` with each scaling mode and an image + video, in three + terminal geometries: square, tall, wide +* `notcurses-demo` with margins +* `notcurses-demo` with FPS plot and HUD up +* Play a game of `notcurses-tetris` +* Run each PoC binary, including `ncpp_build` and `ncpp_build_exceptions` diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index d2c5b49ae..58eab4085 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1438,32 +1438,11 @@ API int ncplane_putwegc_stainable(struct ncplane* n, const wchar_t* gclust, int* // 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 -// of ncplane_putc()). Advances the cursor by some positive number of cells +// of ncplane_putc()). Advances the cursor by some positive number of columns // (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 cells +// On error, a non-positive number is returned, indicating the number of columns // which were written before the error. -static inline int -ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){ - int ret = 0; - // FIXME speed up this blissfully naive solution - while(*gclusters){ - int wcs; - int cols = ncplane_putegc_yx(n, y, x, gclusters, &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; - gclusters += wcs; - ret += wcs; - } - return ret; -} +API int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters); static inline int ncplane_putstr(struct ncplane* n, const char* gclustarr){ @@ -1473,6 +1452,22 @@ ncplane_putstr(struct ncplane* n, const char* gclustarr){ API int ncplane_putstr_aligned(struct ncplane* n, int y, ncalign_e align, const char* s); +// 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 +// of ncplane_putc()). Advances the cursor by some positive number of columns +// (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(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. static inline int ncplane_putwstr_yx(struct ncplane* n, int y, int x, const wchar_t* gclustarr){ @@ -1572,6 +1567,17 @@ ncplane_printf_aligned(struct ncplane* n, int y, ncalign_e align, const char* fo return ret; } +// Write the specified text to the plane, breaking lines sensibly, beginning at +// the specified line. Returns the number of columns written. When breaking a +// line, the line will be cleared to the end of the plane (the last line will +// *not* be so cleared). The number of bytes written from the input is written +// to '*bytes' if it is not NULL. Cleared columns are included in the return +// value, but *not* included in the number of bytes written. Leaves the cursor +// at the end of output. A partial write will be accomplished as far as it can; +// determine whether the write completed by inspecting '*bytes'. +API int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, + const char* text, size_t* bytes); + // Draw horizontal or vertical lines using the specified cell, starting at the // current cursor position. The cursor will end at the cell following the last // cell output (even, perhaps counter-intuitively, when drawing vertical diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index f67ac5ab0..67bb3c780 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -497,6 +497,9 @@ struct ncplane* ncreader_plane(struct ncreader* n); bool ncreader_offer_input(struct ncreader* n, const struct ncinput* ni); char* ncreader_contents(const struct ncreader* n); void ncreader_destroy(struct ncreader* n, char** contents); +int ncplane_puttext(struct ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes); +int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters); +int ncplane_putnstr_aligned(struct ncplane* n, int y, ncalign_e align, size_t s, const char* gclustarr); """) if __name__ == "__main__": diff --git a/src/lib/internal.h b/src/lib/internal.h index c0ecfbab6..9fc941278 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -315,7 +315,7 @@ typedef struct notcurses { // desired margins (best-effort only), copied in from notcurses_options int margin_t, margin_b, margin_r, margin_l; - + int loglevel; palette256 palette; // 256-indexed palette can be used instead of/with RGB bool palette_damage[NCPALETTESIZE]; struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys @@ -757,6 +757,11 @@ calc_gradient_channels(uint64_t* channels, uint64_t ul, uint64_t ur, } } +void nclog(const char* fmt, ...); + +// logging +#define logerror(nc, fmt, ...) do{ if(nc->loglevel >= NCLOGLEVEL_ERROR){ nclog(__FILE__ "%d" fmt, __LINE__, ##__VA_ARGS__); } }while(0); + #ifdef __cplusplus } #endif diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 3c13db3f2..70c3e9366 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -854,7 +854,8 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ term_verify_seq(&ret->tcache.rmcup, "rmcup"); } ret->bottom = ret->top = ret->stdscr = NULL; - if(ncvisual_init(ffmpeg_log_level(opts->loglevel))){ + ret->loglevel = opts->loglevel; + if(ncvisual_init(ffmpeg_log_level(ret->loglevel))){ goto err; } if((ret->stdscr = create_initial_ncplane(ret, dimy, dimx)) == NULL){ @@ -1477,6 +1478,73 @@ int ncplane_hline_interp(ncplane* n, const cell* c, int len, return ret; } +int ncplane_puttext(ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes){ + int totalcols = 0; + // save the beginning for diagnostic + const char* beginning = text; + // text points to the text we have *not* yet output. at each step, we see + // how much space we have available, and begin iterating from text. remember + // the most recent linebreaker that we see. when we exhaust our line, print + // through the linebreaker, and advance text. + // FIXME what about a long word? do we want to leave the big gap? + const int dimx = ncplane_dim_x(n); + const char* linestart = text; + int x = 0; // number of columns consumed for this line + do{ + const char* breaker = NULL; + // figure how much text to output on this line + mbstate_t mbstate = {}; + int width; + // verified columns thus far (carried, and through breaker) + size_t verifiedcols = x; + while(*text && x < dimx - 1){ + wchar_t w; + size_t consumed = mbrtowc(&w, text, MB_CUR_MAX, &mbstate); + if(consumed == (size_t)-2 || consumed == (size_t)-1){ + logerror(n->nc, "Invalid UTF-8 after %zu bytes\n", text - beginning); + *bytes = text - beginning; + return -1; + } + width = wcwidth(w); + if(width < 0){ + logerror(n->nc, "Non-printable UTF-8 after %zu bytes\n", text - beginning); + *bytes = text - beginning; + return -1; + } + if(x + width >= dimx){ + break; + } + // FIXME use the more advanced unicode functionality to break lines + if(iswspace(w)){ + breaker = text; + verifiedcols = x; + } + x += width; + text += consumed; + } + int carrycols = 0; + if(x >= dimx){ + // the last character was one past the amount we can print. set linestart + // back to breaker, set carrycols to the amount since breaker + carrycols = x - verifiedcols; + } + totalcols += verifiedcols; + const int xpos = ncplane_align(n, align, x); + if(breaker == NULL){ + breaker = text; + } + if(ncplane_putnstr_yx(n, y, xpos, breaker - linestart, linestart) < 0){ + *bytes = linestart - beginning; + return -1; + } + x = carrycols; + linestart = breaker + 1; + ++y; // FIXME scrolling! + }while(*text); + *bytes = text - beginning; + return totalcols; +} + int ncplane_vline_interp(ncplane* n, const cell* c, int len, uint64_t c1, uint64_t c2){ unsigned ur, ug, ub; @@ -2097,3 +2165,54 @@ void ncplane_center_abs(const ncplane* n, int* RESTRICT y, int* RESTRICT x){ *x += n->absx; } } + +void nclog(const char* fmt, ...){ + va_list va; + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + +int ncplane_putstr_yx(struct ncplane* n, int y, int x, const char* gclusters){ + int ret = 0; + // FIXME speed up this blissfully naive solution + while(*gclusters){ + int wcs; + int cols = ncplane_putegc_yx(n, y, x, gclusters, &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; + gclusters += wcs; + ret += wcs; + } + return ret; +} + +int ncplane_putnstr_yx(struct ncplane* n, int y, int x, size_t s, const char* gclusters){ + size_t ret = 0; + // FIXME speed up this blissfully naive solution + while(ret < s && *gclusters){ + int wcs; + int cols = ncplane_putegc_yx(n, y, x, gclusters, &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; + gclusters += wcs; + ret += wcs; + } + return ret; +} diff --git a/src/lib/render.c b/src/lib/render.c index 8fbd2d6f6..ec1beda70 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -120,7 +120,11 @@ void cell_release(ncplane* n, cell* c){ // Duplicate one cell onto another when they share a plane. Convenience wrapper. int cell_duplicate(ncplane* n, cell* targ, const cell* c){ - return cell_duplicate_far(&n->pool, targ, n, c); + int r = cell_duplicate_far(&n->pool, targ, n, c); + if(r < 0){ + logerror(n->nc, "Failed duplicating cell"); + } + return r; } // the heart of damage detection. compare two cells (from two different planes) diff --git a/tests/layout.cpp b/tests/layout.cpp new file mode 100644 index 000000000..3a6c08f9d --- /dev/null +++ b/tests/layout.cpp @@ -0,0 +1,50 @@ +#include "main.h" +#include "internal.h" + +TEST_CASE("TextLayout") { + notcurses_options nopts{}; + notcurses* nc_ = notcurses_init(&nopts, nullptr); + if(!nc_){ + return; + } + ncplane* ncp_ = notcurses_stdplane(nc_); + REQUIRE(ncp_); + + const char str[] = "this is going to be broken up"; + + SUBCASE("LayoutLeft") { + auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr); + REQUIRE(sp); + size_t bytes; + CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_LEFT, str, &bytes)); + CHECK(0 == notcurses_render(nc_)); + CHECK(bytes == strlen(str)); + // FIXME inspect layout + ncplane_destroy(sp); + } + + SUBCASE("LayoutRight") { + auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr); + REQUIRE(sp); + size_t bytes; + CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_RIGHT, str, &bytes)); + CHECK(0 == notcurses_render(nc_)); + CHECK(bytes == strlen(str)); + // FIXME inspect layout + ncplane_destroy(sp); + } + + SUBCASE("LayoutCenter") { + auto sp = ncplane_new(nc_, 2, 20, 0, 0, nullptr); + REQUIRE(sp); + size_t bytes; + CHECK(0 < ncplane_puttext(sp, 0, NCALIGN_CENTER, str, &bytes)); + CHECK(0 == notcurses_render(nc_)); + CHECK(bytes == strlen(str)); + // FIXME inspect layout + ncplane_destroy(sp); + } + + CHECK(0 == notcurses_stop(nc_)); + +}