diff --git a/include/notcurses.h b/include/notcurses.h index 297be8ec9..895e625f9 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -211,7 +211,7 @@ int notcurses_palette_size(const struct notcurses* nc); // Working with cells // Breaks the UTF-8 string in 'gcluster' down, setting up the cell 'c'. -int cell_load(cell* c, const char* gcluster); +int cell_load(struct ncplane* n, cell* c, const char* gcluster); static inline uint32_t cell_fg_rgb(uint64_t channel){ diff --git a/src/bin/demo.c b/src/bin/demo.c index d7f9ff3ab..e61345484 100644 --- a/src/bin/demo.c +++ b/src/bin/demo.c @@ -28,14 +28,15 @@ int main(void){ int x, y, rows, cols; ncplane_dimyx(ncp, &rows, &cols); cell c; - cell_load(&c, /*L"💣*/"X"); + const char* cstr = "✓"; + cell_load(ncp, &c, cstr); cell_set_fg(&c, 200, 0, 200); for(y = 1 ; y < rows - 1 ; ++y){ if(ncplane_cursor_move_yx(ncp, y, 1)){ goto err; } for(x = 1 ; x < cols - 1 ; ++x){ - if(ncplane_putc(ncp, &c, NULL)){ + if(ncplane_putc(ncp, &c, cstr) != (int)strlen(cstr)){ goto err; } } @@ -44,7 +45,7 @@ int main(void){ goto err; } sleep(1); - const char str[] = "Wovon man nicht sprechen kann, darüber muss man schweigen."; + const char str[] = " Wovon man nicht sprechen kann, darüber muss man schweigen. "; if(ncplane_cursor_move_yx(ncp, y / 2, (x - strlen(str)) / 2)){ goto err; } diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 5f628dea2..0242e1ccf 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -42,6 +42,8 @@ typedef struct ncplane { struct notcurses* nc; // our parent nc, kinda lame waste of memory FIXME uint64_t channels; // colors when not provided an active style char* pool; // storage pool for multibyte grapheme clusters + size_t poolsize; // bytes allocated for gcluster pool + size_t poolwrite;// where next to write into the pool } ncplane; typedef struct notcurses { @@ -126,6 +128,7 @@ term_verify_seq(char** gseq, const char* name){ static void free_plane(ncplane* p){ if(p){ + free(p->pool); free(p->fb); free(p); } @@ -165,6 +168,15 @@ create_ncplane(notcurses* nc, int rows, int cols){ return p; } +static int +init_gcluster_pool(ncplane* p){ + // FIXME VERY rough proof of concept, do not merge! + p->poolsize = BUFSIZ * p->leny; + p->pool = malloc(p->poolsize); + p->poolwrite = 0; + return 0; +} + // Call this on initialization, or when the screen size changes. Takes a flat // array of *rows * *cols cells (may be NULL if *rows == *cols == 0). Gets the // new size, and copies what can be copied from the old stdscr. Assumes that @@ -185,7 +197,9 @@ alloc_stdscr(notcurses* nc){ if((p = create_ncplane(nc, rows, cols)) == NULL){ goto err; } - p->pool = NULL; + if(init_gcluster_pool(p)){ + goto err; + } ncplane** oldscr; ncplane* preserve; // if we ever make this a doubly-linked list, turn this into o(1) @@ -385,10 +399,6 @@ int notcurses_stop(notcurses* nc){ return ret; } -// FIXME don't go using STDOUT_FILENO here, we want nc->outfd! -// only works with string literals! -#define strout(x) write(STDOUT_FILENO, (x), sizeof(x)) - static int erpchar(int c){ if(write(STDOUT_FILENO, &c, 1) == 1){ @@ -507,6 +517,7 @@ term_movyx(int y, int x){ } // is it a single ASCII byte, wholly contained within the cell? +// FIXME need check that next character is visible mainly static inline bool simple_gcluster_p(const char* gcluster){ return *gcluster == '\0' || (*(unsigned char*)gcluster < 0x80 && gcluster[1] == '\0'); @@ -548,6 +559,16 @@ term_putc(const notcurses* nc, const ncplane* n, const cell* c){ return 0; } +static void +advance_cursor(ncplane* n){ + if(++n->x == n->lenx){ + n->x = 0; + if(++n->y == n->leny){ + n->y = 0; + } + } +} + // FIXME this needs to keep an invalidation bitmap, rather than blitting the // world every time int notcurses_render(notcurses* nc){ @@ -598,31 +619,31 @@ void ncplane_cursor_yx(const ncplane* n, int* y, int* x){ } } -static void -advance_cursor(ncplane* n){ - if(++n->x == n->lenx){ - n->x = 0; - if(++n->y == n->leny){ - n->y = 0; - } - } -} - int ncplane_putc(ncplane* n, const cell* c, const char* gclust){ cell* targ = &n->fb[fbcellidx(n, n->y, n->x)]; memcpy(targ, c, sizeof(*c)); - cell_load(targ, gclust); - return 0; + int ret = cell_load(n, targ, gclust); + advance_cursor(n); + return ret; } -int cell_load(cell* c, const char* gcluster){ +int cell_load(ncplane* n, cell* c, const char* gcluster){ if(simple_gcluster_p(gcluster)){ c->gcluster = *gcluster; +fprintf(stderr, "SIMPLE! %c %d\n", c->gcluster, !!c->gcluster); return !!c->gcluster; } - // FIXME gotta copy gcluster into storage pool, up through terminator OR - // next spacing character... - return strlen(gcluster); + const char* end = gcluster + 1; + while(*(const unsigned char*)end >= 0x80){ // FIXME broken broken broken + ++end; + } +fprintf(stderr, "END: %p G: %p DIFF: %zu %s\n", end, gcluster, end - gcluster, gcluster); + // FIXME enlarge pool on demand + memcpy(n->pool + n->poolwrite, gcluster, end - gcluster); + c->gcluster = n->poolwrite + 0x80; + n->poolwrite += end - gcluster; + n->pool[n->poolwrite++] = '\0'; + return end - gcluster; } int ncplane_putstr(ncplane* n, const char* gcluster){ @@ -634,20 +655,29 @@ int ncplane_putstr(ncplane* n, const char* gcluster){ cell_set_fg(&c, cell_rgb_red(rgb), cell_rgb_green(rgb), cell_rgb_blue(rgb)); rgb = cell_bg_rgb(n->channels); cell_set_bg(&c, cell_rgb_red(rgb), cell_rgb_green(rgb), cell_rgb_blue(rgb)); + int wcs = 0; while(*gcluster){ - int wcs = cell_load(&c, gcluster); +fprintf(stderr, "wcs: %d gcluster: %s ret: %d\n", wcs, gcluster, ret); + wcs = cell_load(n, &c, gcluster); if(wcs < 0){ return -ret; } if(wcs == 0){ break; } - gcluster += wcs; - if(ncplane_putc(n, &c, extended_gcluster(n, &c))){ + wcs = ncplane_putc(n, &c, gcluster); + if(wcs < 0){ +fprintf(stderr, "wcsnew: %d\n", wcs); return -ret; } - ++ret; + if(wcs == 0){ + break; + } +fprintf(stderr, "wcsnew: %d\n", wcs); + gcluster += wcs; + ret += wcs; } +fprintf(stderr, "RETURNING %d\n", ret); return ret; } diff --git a/tests/ncplane.cpp b/tests/ncplane.cpp index b68282bc5..9cd990dd8 100644 --- a/tests/ncplane.cpp +++ b/tests/ncplane.cpp @@ -104,20 +104,21 @@ TEST_F(NcplaneTest, RejectBadRGB) { TEST_F(NcplaneTest, EmitWchar) { const char cchar[] = "✔"; cell c; - cell_load(&c, cchar); - EXPECT_EQ(0, ncplane_putc(n_, &c, cchar)); + cell_load(n_, &c, cchar); + EXPECT_EQ(strlen(cchar), ncplane_putc(n_, &c, cchar)); int x, y; ncplane_cursor_yx(n_, &y, &x); - EXPECT_EQ(y, 0); - EXPECT_EQ(x, 1); + EXPECT_EQ(0, y); + EXPECT_EQ(1, x); } // Verify we can emit a wide string, and it advances the cursor TEST_F(NcplaneTest, EmitStr) { const char s[] = "Σιβυλλα τι θελεις; respondebat illa: αποθανειν θελω."; - EXPECT_EQ(strlen(s), ncplane_putstr(n_, s)); + int wrote = ncplane_putstr(n_, s); + EXPECT_EQ(strlen(s), wrote); int x, y; ncplane_cursor_yx(n_, &y, &x); - EXPECT_EQ(y, 0); - // FIXME EXPECT_EQ(x, wcswidth(wstr, wcslen(wstr))); + EXPECT_EQ(0, y); + EXPECT_NE(1, x); // FIXME tighten in on this }