notcurses_puttext: use libunistring for wordbreaking #772

This commit is contained in:
nick black 2020-07-22 03:30:02 -04:00 committed by Nick Black
parent 0ba1512d68
commit 604747c8f2
7 changed files with 38 additions and 16 deletions

View File

@ -79,18 +79,24 @@ elseif(${USE_OIIO})
pkg_check_modules(OIIO REQUIRED OpenImageIO>=2.1) pkg_check_modules(OIIO REQUIRED OpenImageIO>=2.1)
endif() endif()
find_library(MATH_LIBRARIES m) find_library(MATH_LIBRARIES m)
# don't cache this, or installing it requires clearing the cache to be found if(${USE_DOCTEST})
find_package(doctest 2.3.5 REQUIRED)
endif()
# don't cache these, or installing them requires clearing the cache to be found.
# this is going to be true for anything lacking pkg-config/CMake support.
unset(HAVE_QRCODEGEN_H CACHE) unset(HAVE_QRCODEGEN_H CACHE)
check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H) check_include_file("uniwbrk.h" HAVE_UNISTRING_H)
if(NOT "${HAVE_UNISTRING_H}")
message(FATAL_ERROR "Couldn't find uniwbrk.h from GNU libunistring")
endif()
if("${USE_QRCODEGEN}") if("${USE_QRCODEGEN}")
check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H)
if(NOT "${HAVE_QRCODEGEN_H}") if(NOT "${HAVE_QRCODEGEN_H}")
message(FATAL_ERROR "USE_QRCODEGEN is active, but couldn't find qrcodegen.h") message(FATAL_ERROR "USE_QRCODEGEN is active, but couldn't find qrcodegen.h")
endif() endif()
endif() endif()
find_library(LIBRT rt) find_library(LIBRT rt)
if(${USE_DOCTEST})
find_package(doctest 2.3.5 REQUIRED)
endif()
# libnotcurses (core shared library and static library) # libnotcurses (core shared library and static library)
file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp) file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp)
@ -129,6 +135,7 @@ target_link_libraries(notcurses
PRIVATE PRIVATE
"${TERMINFO_LIBRARIES}" "${TERMINFO_LIBRARIES}"
"${LIBRT}" "${LIBRT}"
unistring
PUBLIC PUBLIC
Threads::Threads Threads::Threads
) )
@ -136,6 +143,7 @@ target_link_libraries(notcurses-static
PRIVATE PRIVATE
"${TERMINFO_STATIC_LIBRARIES}" "${TERMINFO_STATIC_LIBRARIES}"
"${LIBRT}" "${LIBRT}"
unistring
PUBLIC PUBLIC
Threads::Threads Threads::Threads
) )

View File

@ -1,6 +1,9 @@
This document attempts to list user-visible changes and any major internal This document attempts to list user-visible changes and any major internal
rearrangements of Notcurses. rearrangements of Notcurses.
* 1.6.7 (not yet released)
* GNU libunistring is now required to build/load Notcurses.
* 1.6.6 (2020-07-19) * 1.6.6 (2020-07-19)
* `notcurses-pydemo` is now only installed alongside the Python module, * `notcurses-pydemo` is now only installed alongside the Python module,
using setuptools. CMake no longer installs it. using setuptools. CMake no longer installs it.

View File

@ -114,6 +114,7 @@ that fine library.
* (build) A C11 and a C++17 compiler * (build) A C11 and a C++17 compiler
* (build) CMake 3.14.0+ * (build) CMake 3.14.0+
* (build+runtime) From NCURSES: terminfo 6.1+ * (build+runtime) From NCURSES: terminfo 6.1+
* (build+runtime) GNU libunistring 0.9.10+
* (OPTIONAL) (build+runtime) From QR-Code-generator: [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+ * (OPTIONAL) (build+runtime) From QR-Code-generator: [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+
* (OPTIONAL) (build+runtime) From [FFmpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+ * (OPTIONAL) (build+runtime) From [FFmpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+
* (OPTIONAL) (build+runtime) [OpenImageIO](https://github.com/OpenImageIO/oiio) 2.15.0+ * (OPTIONAL) (build+runtime) [OpenImageIO](https://github.com/OpenImageIO/oiio) 2.15.0+

View File

@ -341,7 +341,7 @@ nc_err_e ncdirect_render_image(ncdirect* n, const char* file, ncalign_e align,
struct ncplane* faken = ncplane_create(nullptr, nullptr, struct ncplane* faken = ncplane_create(nullptr, nullptr,
disprows / encoding_y_scale(bset), disprows / encoding_y_scale(bset),
dispcols / encoding_x_scale(bset), dispcols / encoding_x_scale(bset),
0, 0, nullptr); 0, 0, nullptr, nullptr);
if(faken == nullptr){ if(faken == nullptr){
return NCERR_NOMEM; return NCERR_NOMEM;
} }

View File

@ -80,6 +80,7 @@ typedef struct ncplane {
cell basecell; // cell written anywhere that fb[i].gcluster == 0 cell basecell; // cell written anywhere that fb[i].gcluster == 0
struct notcurses* nc; // notcurses object of which we are a part struct notcurses* nc; // notcurses object of which we are a part
bool scrolling; // is scrolling enabled? always disabled by default bool scrolling; // is scrolling enabled? always disabled by default
char* name; // used only for debugging
} ncplane; } ncplane;
#include "blitset.h" #include "blitset.h"
@ -782,7 +783,7 @@ calc_gradient_channels(uint64_t* channels, uint64_t ul, uint64_t ur,
// ncvisual_render(), and thus calls these low-level internal functions. // ncvisual_render(), and thus calls these low-level internal functions.
// they are not for general use -- check ncplane_new() and ncplane_destroy(). // they are not for general use -- check ncplane_new() and ncplane_destroy().
ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols, ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols,
int yoff, int xoff, void* opaque); int yoff, int xoff, void* opaque, const char* name);
void free_plane(ncplane* p); void free_plane(ncplane* p);
// heap-allocated formatted output // heap-allocated formatted output

View File

@ -14,6 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <signal.h> #include <signal.h>
#include <locale.h> #include <locale.h>
#include <uniwbrk.h>
#include <langinfo.h> #include <langinfo.h>
#include <stdatomic.h> #include <stdatomic.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
@ -265,6 +266,7 @@ void free_plane(ncplane* p){
p->nc->stats.fbbytes -= sizeof(*p->fb) * p->leny * p->lenx; p->nc->stats.fbbytes -= sizeof(*p->fb) * p->leny * p->lenx;
} }
egcpool_dump(&p->pool); egcpool_dump(&p->pool);
free(p->name);
free(p->fb); free(p->fb);
free(p); free(p);
} }
@ -281,7 +283,7 @@ void free_plane(ncplane* p){
// ncplane created by ncdirect for rendering visuals. in that case (and only in // ncplane created by ncdirect for rendering visuals. in that case (and only in
// that case), nc is NULL. // that case), nc is NULL.
ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols, ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols,
int yoff, int xoff, void* opaque){ int yoff, int xoff, void* opaque, const char* name){
if(rows <= 0 || cols <= 0){ if(rows <= 0 || cols <= 0){
return NULL; return NULL;
} }
@ -299,6 +301,7 @@ ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols,
p->x = p->y = 0; p->x = p->y = 0;
p->logrow = 0; p->logrow = 0;
p->blist = NULL; p->blist = NULL;
p->name = name ? strdup(name) : NULL;
if( (p->boundto = n) ){ if( (p->boundto = n) ){
p->absx = xoff + n->absx; p->absx = xoff + n->absx;
p->absy = yoff + n->absy; p->absy = yoff + n->absy;
@ -339,7 +342,8 @@ ncplane* ncplane_create(notcurses* nc, ncplane* n, int rows, int cols,
static ncplane* static ncplane*
create_initial_ncplane(notcurses* nc, int dimy, int dimx){ create_initial_ncplane(notcurses* nc, int dimy, int dimx){
nc->stdplane = ncplane_create(nc, NULL, dimy - (nc->margin_t + nc->margin_b), nc->stdplane = ncplane_create(nc, NULL, dimy - (nc->margin_t + nc->margin_b),
dimx - (nc->margin_l + nc->margin_r), 0, 0, NULL); dimx - (nc->margin_l + nc->margin_r), 0, 0, NULL,
"std");
return nc->stdplane; return nc->stdplane;
} }
@ -352,16 +356,17 @@ const ncplane* notcurses_stdplane_const(const notcurses* nc){
} }
ncplane* ncplane_new(notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque){ ncplane* ncplane_new(notcurses* nc, int rows, int cols, int yoff, int xoff, void* opaque){
return ncplane_create(nc, NULL, rows, cols, yoff, xoff, opaque); return ncplane_create(nc, NULL, rows, cols, yoff, xoff, opaque, NULL);
} }
ncplane* ncplane_bound(ncplane* n, int rows, int cols, int yoff, int xoff, void* opaque){ ncplane* ncplane_bound(ncplane* n, int rows, int cols, int yoff, int xoff, void* opaque){
return ncplane_create(n->nc, n, rows, cols, yoff, xoff, opaque); return ncplane_create(n->nc, n, rows, cols, yoff, xoff, opaque, NULL);
} }
ncplane* ncplane_aligned(ncplane* n, int rows, int cols, int yoff, ncplane* ncplane_aligned(ncplane* n, int rows, int cols, int yoff,
ncalign_e align, void* opaque){ ncalign_e align, void* opaque){
return ncplane_create(n->nc, n, rows, cols, yoff, ncplane_align(n, align, cols), opaque); return ncplane_create(n->nc, n, rows, cols, yoff,
ncplane_align(n, align, cols), opaque, NULL);
} }
void ncplane_home(ncplane* n){ void ncplane_home(ncplane* n){
@ -408,7 +413,8 @@ ncplane* ncplane_dup(const ncplane* n, void* opaque){
const struct notcurses* nc = ncplane_notcurses_const(n); const struct notcurses* nc = ncplane_notcurses_const(n);
const int placey = n->absy - nc->margin_t; const int placey = n->absy - nc->margin_t;
const int placex = n->absx - nc->margin_l; const int placex = n->absx - nc->margin_l;
ncplane* newn = ncplane_create(n->nc, n->boundto, dimy, dimx, placey, placex, opaque); ncplane* newn = ncplane_create(n->nc, n->boundto, dimy, dimx,
placey, placex, opaque, n->name);
if(newn){ if(newn){
if(egcpool_dup(&newn->pool, &n->pool)){ if(egcpool_dup(&newn->pool, &n->pool)){
ncplane_destroy(newn); ncplane_destroy(newn);
@ -1595,10 +1601,10 @@ int ncplane_hline_interp(ncplane* n, const cell* c, int len,
return ret; return ret;
} }
// FIXME there are more advanced means of wordbreaking within unicode; use them
static bool static bool
iswordbreak(wchar_t w){ iswordbreak(wchar_t wchar){
return iswspace(w); int w = uc_wordbreak_property(wchar);
return (w == WBP_OTHER || w == WBP_NEWLINE || w == WBP_CR || w == WBP_LF);
} }
static bool static bool
@ -1623,6 +1629,7 @@ overlong_word(const char* text, int dimx){
return false; return false;
} }
// FIXME probably best to use u8_wordbreaks() and get all wordbreaks at once...
int ncplane_puttext(ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes){ int ncplane_puttext(ncplane* n, int y, ncalign_e align, const char* text, size_t* bytes){
int totalcols = 0; int totalcols = 0;
// save the beginning for diagnostic // save the beginning for diagnostic

View File

@ -498,6 +498,7 @@ TEST_CASE("NCPlane") {
ncplane_cursor_yx(n_, &y, &x); ncplane_cursor_yx(n_, &y, &x);
REQUIRE(1 == y); REQUIRE(1 == y);
REQUIRE(dimx == x); REQUIRE(dimx == x);
// this ought not print anything, since we're at the end of the row
REQUIRE(0 == ncplane_putstr(n_, STR3)); REQUIRE(0 == ncplane_putstr(n_, STR3));
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0)); REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
REQUIRE(0 < ncplane_at_cursor_cell(n_, &testcell)); // want first char of STR1 REQUIRE(0 < ncplane_at_cursor_cell(n_, &testcell)); // want first char of STR1
@ -534,6 +535,7 @@ TEST_CASE("NCPlane") {
ncplane_cursor_yx(n_, &y, &x); ncplane_cursor_yx(n_, &y, &x);
REQUIRE(1 == y); REQUIRE(1 == y);
REQUIRE(dimx == x); REQUIRE(dimx == x);
// this ought not print anything, since we're at the end of the row
REQUIRE(0 == ncplane_putstr(n_, STR3)); REQUIRE(0 == ncplane_putstr(n_, STR3));
REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0)); REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0));
REQUIRE(0 < ncplane_at_cursor_cell(n_, &testcell)); // want first char of STR1 REQUIRE(0 < ncplane_at_cursor_cell(n_, &testcell)); // want first char of STR1