From 4d6526a61d65ebc1983aff4dbed3e220d25e39c4 Mon Sep 17 00:00:00 2001 From: nick black Date: Thu, 12 Aug 2021 02:36:36 -0400 Subject: [PATCH] ncneofetch rewritten in CLI mode ncneofetch was previously direct mode followed by rendered mode with margins, a Frankenstein application if one ever existed. Rewrite it using CLI mode, extending the latter as necessary to accomplish this task. We now have one fewer dependency on direct mode, we have better proven out CLI mode, and we get a ~30% reduction in ncneofetch runtime. Good stuff! Closes #2030. Add ncplane_scrollup() and ncplane_scrollup_child() Cleans up ncport.h Eliminates some inconsequential memory leaks in ncneofetch Add SPRIXEL_UNSEEN to avoid invalid moves on not-yet-displayed sprixels --- NEWS.md | 4 +++ USAGE.md | 9 +++++++ doc/man/man3/notcurses_plane.3.md | 4 +++ include/notcurses/ncport.h | 41 +++++++++++++------------------ include/notcurses/notcurses.h | 19 ++++++++++++-- src/compat/compat.h | 5 ++++ src/demo/unicodeblocks.c | 8 +++--- src/fetch/main.c | 38 +++++++++++++++++++--------- src/lib/kitty.c | 4 +-- src/lib/notcurses.c | 37 ++++++++++++++++++++++++++++ src/lib/render.c | 9 ++++--- src/lib/sixel.c | 6 +++++ src/lib/sprite.c | 4 +-- src/lib/sprite.h | 3 ++- src/poc/ncwidth.c | 1 + 15 files changed, 141 insertions(+), 51 deletions(-) diff --git a/NEWS.md b/NEWS.md index 97e2518da..3a35409d2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,10 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. * 2.3.14 (not yet released) + * `ncneofetch` has been changed to use "CLI mode" instead of Direct Mode, + as a proof of concept. It is very likely that Direct Mode will be + deprecated for ABI3. New code ought not be written using it. + * Added `ncplane_scrollup()` and `ncplane_scrollup_child()`. * Fixed grotesque errors in `ncplane_set_*_palindex()`. * 2.3.13 (2021-08-04) diff --git a/USAGE.md b/USAGE.md index 3e608825f..24c7a0a45 100644 --- a/USAGE.md +++ b/USAGE.md @@ -883,6 +883,15 @@ scrolling is enabled). // controlled with ncplane_set_scrolling(). Returns true if scrolling was // previously enabled, or false if it was disabled. bool ncplane_set_scrolling(struct ncplane* n, bool scrollp); + +// Effect |r| scroll events on the plane |n|. Returns an error if |n| is not +// a scrolling plane, and otherwise returns the number of lines scrolled. +int ncplane_scrollup(struct ncplane* n, int r); + +// Scroll |n| up until |child| is no longer hidden beneath it. Returns an +// error if |child| is not a child of |n|, or |n| is not scrolling, or |child| +// is fixed. Returns the number of scrolling events otherwise (might be 0). +int ncplane_scrollup_child(struct ncplane* n, const struct ncplane* child); ``` Planes can be freely resized, though they must retain a positive size in diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index 566cb3315..07be0063f 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -209,6 +209,10 @@ typedef struct ncplane_options { **bool ncplane_scrolling_p(const struct ncplane* ***n***);** +**int ncplane_scrollup(struct ncplane* ***n***, int ***r***);** + +**int ncplane_scrollup_child(struct ncplane* ***n***, const struct ncplane* ***child***);** + **int ncplane_rotate_cw(struct ncplane* ***n***);** **int ncplane_rotate_ccw(struct ncplane* ***n***);** diff --git a/include/notcurses/ncport.h b/include/notcurses/ncport.h index ec94187b2..f36d4ccc2 100644 --- a/include/notcurses/ncport.h +++ b/include/notcurses/ncport.h @@ -5,37 +5,30 @@ extern "C" { #endif -// 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). -#if defined(__linux__) || defined(__gnu_hurd__) -#include -#include -#include -#include -#define htole(x) (__bswap_32(htonl(x))) -#elif defined(__APPLE__) -#include -#include -#include -#include -#define htole(x) (OSSwapInt32(htonl(x))) -#elif defined(__MINGW64__) -#define htole(x) (x) // FIXME +// Platform-dependent preprocessor material (includes and definitions) needed +// to compile against Notcurses. A critical definition is htole(), which forces +// 32-bit values to little-endian (as used in the nccell gcluster field). This +// ought be defined so that it's a a no-op on little-endian builds. + +#if defined(__MINGW64__) // Windows // FIXME placeholders, need real solutions here #define wcwidth(w) 1 -#define wcswidth(w, s) (s) -#define sigset_t int -#define sigemptyset(x) -#define O_CLOEXEC O_NOINHERIT -#define O_NONBLOCK 0 -#define O_DIRECTORY 0 -#define S_IFLNK 0 -#else // bsd +#define htole(x) (x) // FIXME are all windows installs LE? +#else // Non-Windows, UNIX-common #include #include +#include +#if defined(__linux__) || defined(__gnu_hurd__) // Linux/Hurd +#include +#define htole(x) (__bswap_32(htonl(x))) +#elif defined(__APPLE__) // macOS +#include +#define htole(x) (OSSwapInt32(htonl(x))) +#else // BSD #include #define htole(x) (bswap32(htonl(x))) #endif +#endif #ifdef __cplusplus } // extern "C" diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index bd1274e1e..9c3ebb75f 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1570,8 +1570,23 @@ API int ncplane_move_below(struct ncplane* RESTRICT n, struct ncplane* RESTRICT below); // Return the plane below this one, or NULL if this is at the bottom. -API struct ncplane* ncplane_below(struct ncplane* n); -API struct ncplane* ncplane_above(struct ncplane* n); +API struct ncplane* ncplane_below(struct ncplane* n) + __attribute__ ((nonnull (1))); + +// Return the plane above this one, or NULL if this is at the top. +API struct ncplane* ncplane_above(struct ncplane* n) + __attribute__ ((nonnull (1))); + +// Effect |r| scroll events on the plane |n|. Returns an error if |n| is not +// a scrolling plane, and otherwise returns the number of lines scrolled. +API int ncplane_scrollup(struct ncplane* n, int r) + __attribute__ ((nonnull (1))); + +// Scroll |n| up until |child| is no longer hidden beneath it. Returns an +// error if |child| is not a child of |n|, or |n| is not scrolling, or |child| +// is fixed. Returns the number of scrolling events otherwise (might be 0). +API int ncplane_scrollup_child(struct ncplane* n, const struct ncplane* child) + __attribute__ ((nonnull (1, 2))); // Rotate the plane π/2 radians clockwise or counterclockwise. This cannot // be performed on arbitrary planes, because glyphs cannot be arbitrarily diff --git a/src/compat/compat.h b/src/compat/compat.h index e89f4f179..aa8fcb19f 100644 --- a/src/compat/compat.h +++ b/src/compat/compat.h @@ -17,6 +17,7 @@ extern "C" { #ifdef __MINGW64__ #include +#define wcswidth(w, s) (s) #define tcgetattr(x, y) -1 #define tcsetattr(x, y, z) -1 #define ECHO 0 @@ -27,6 +28,10 @@ extern "C" { #define TCSAFLUSH 0 #define TCSANOW 0 #define O_NOCTTY 0 +#define O_CLOEXEC O_NOINHERIT +#define O_NONBLOCK 0 +#define O_DIRECTORY 0 +#define S_IFLNK 0 #define SA_SIGINFO 0 #define SA_RESETHAND 0 #define SIGQUIT 0 diff --git a/src/demo/unicodeblocks.c b/src/demo/unicodeblocks.c index b9b16b6c0..c10ca8920 100644 --- a/src/demo/unicodeblocks.c +++ b/src/demo/unicodeblocks.c @@ -200,8 +200,8 @@ int unicodeblocks_demo(struct notcurses* nc){ { .name = "CJK Compatibility Ideographs, Alphabetic Presentation Forms", .start = 0xfa00, }, { .name = "Arabic Presentation Forms-A", .start = 0xfc00, }, { .name = "Halfwidth and Fullwidth Forms", .start = 0xfe00, }, - { .name = "Linear B Syllabary, Linear B Ideograms, Aegean Numbers, Phaistos Disc", .start = 0x10000, }, - { .name = "Lycian, Carian, Coptic Epact Numbers, Old Italic, Gothic, Old Permic", .start = 0x10200, }, + { .name = "Linear B Syllabary, Linear B Ideograms, Aegean, Phaistos Disc", .start = 0x10000, }, + { .name = "Lycian, Carian, Coptic Epact, Old Italic, Gothic, Old Permic", .start = 0x10200, }, { .name = "Cuneiform", .start = 0x12000, }, { .name = "Cuneiform (cont.)", .start = 0x12200, }, { .name = "Byzantine Musical Symbols, Musical Symbols", .start = 0x1d000, }, @@ -278,10 +278,10 @@ int unicodeblocks_demo(struct notcurses* nc){ if(ncplane_set_fg_rgb8(n, 0x40, 0xc0, 0x40)){ return -1; } - if(ncplane_cursor_move_yx(n, 6 + BLOCKSIZE / CHUNKSIZE, 2)){ + if(ncplane_cursor_move_yx(n, 6 + BLOCKSIZE / CHUNKSIZE, 4)){ return -1; } - if(ncplane_printf(n, "%*.*s", maxx - 4, maxx - 4, "") <= 0){ + if(ncplane_printf(n, "%*.*s", maxx - 8, maxx - 8, "") <= 0){ return -1; } if(ncplane_printf_aligned(n, 6 + BLOCKSIZE / CHUNKSIZE, NCALIGN_CENTER, "%s", description) <= 0){ diff --git a/src/fetch/main.c b/src/fetch/main.c index 64dcc1648..2a83c0291 100644 --- a/src/fetch/main.c +++ b/src/fetch/main.c @@ -48,7 +48,12 @@ typedef struct fetched_info { static void free_fetched_info(fetched_info* fi){ free(fi->cpu_model); + free(fi->hostname); free(fi->username); + free(fi->kernel); + free(fi->kernver); + free(fi->distro_pretty); + free(fi->term); } static int @@ -337,7 +342,7 @@ xnu_ncneofetch(fetched_info* fi){ .logofile = "/System/Library/PrivateFrameworks/LoginUIKit.framework/Versions/A/Frameworks/LoginUICore.framework/Versions/A/Resources/apple@2x.png", }; fi->neologo = get_neofetch_art("Darwin"); - fi->distro_pretty = "OS X 11.4 (Big Sur)"; // FIXME + fi->distro_pretty = strdup("OS X 11.4 (Big Sur)"); // FIXME return &fbsd; } @@ -380,9 +385,11 @@ static int infoplane_notcurses(struct notcurses* nc, const fetched_info* fi, int planeheight){ const int planewidth = 72; int dimy; + int y; struct ncplane* std = notcurses_stddim_yx(nc, &dimy, NULL); + ncplane_cursor_yx(std, &y, NULL); struct ncplane_options nopts = { - .y = dimy - planeheight, + .y = y, .x = NCALIGN_CENTER, .rows = planeheight, .cols = planewidth, @@ -428,7 +435,6 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi, int planeheigh }else{ ncplane_printf_aligned(infop, 4, NCALIGN_LEFT, " TERM: %s", fi->term); } - free(fi->term); ncplane_printf_aligned(infop, 4, NCALIGN_RIGHT, "Screen0: %dx%d ", fi->dimx, fi->dimy); ncplane_printf_aligned(infop, 5, NCALIGN_LEFT, " LANG: %s", fi->lang); #ifndef __MINGW64__ @@ -469,6 +475,7 @@ infoplane_notcurses(struct notcurses* nc, const fetched_info* fi, int planeheigh ncchannels_set_fg_rgb8(&channels, 0, 0, 0); ncchannels_set_bg_rgb8(&channels, 0x50, 0x50, 0x50); ncplane_set_base(infop, " ", 0, channels); + ncplane_scrollup_child(std, infop); if(notcurses_render(nc)){ return -1; } @@ -522,7 +529,7 @@ neologo_present(struct notcurses* nc, const char* nlogo){ struct ncplane* n = notcurses_stddim_yx(nc, &dimy, &dimx); const int leftpad = (dimx - maxlinelen) / 2; for(int i = 0 ; i < linecount ; ++i){ - printf("%*.*s%s", leftpad, leftpad, "", lines[i]); + ncplane_printf(n, "%*.*s%s", leftpad, leftpad, "", lines[i]); free(lines[i]); } free(lines); @@ -542,6 +549,8 @@ display_thread(void* vmarshal){ struct marshal* m = vmarshal; drawpalette(m->nc); notcurses_render(m->nc); + ncplane_set_bg_default(notcurses_stdplane(m->nc)); + ncplane_set_fg_default(notcurses_stdplane(m->nc)); // we've just rendered, so any necessary scrolling has been performed. draw // our image wherever the palette ended, and then scroll as necessary to // make that new plane visible. @@ -553,6 +562,8 @@ display_thread(void* vmarshal){ ncv = ncvisual_from_file(m->dinfo->logofile); } if(ncv){ + int y; + ncplane_cursor_yx(notcurses_stdplane_const(m->nc), &y, NULL); struct ncvisual_options vopts = { .x = NCALIGN_CENTER, .blitter = NCBLIT_PIXEL, @@ -561,7 +572,14 @@ display_thread(void* vmarshal){ }; struct ncplane* iplane = ncvisual_render(m->nc, ncv, &vopts); ncvisual_destroy(ncv); - notcurses_render(m->nc); + if(iplane){ + ncplane_move_yx(iplane, y, 0); + ncplane_scrollup_child(notcurses_stdplane(m->nc), iplane); + notcurses_render(m->nc); + ncplane_cursor_move_yx(notcurses_stdplane(m->nc), + ncplane_abs_y(iplane) + ncplane_dim_y(iplane), 0); + return NULL; + } } } if(m->neologo){ @@ -627,13 +645,11 @@ ncneofetch(struct notcurses* nc){ } int main(void){ - if(setlocale(LC_ALL, "") == NULL){ - fprintf(stderr, "Warning: couldn't set locale based off LANG\n"); - } struct notcurses_options opts = { - .flags = NCOPTION_SUPPRESS_BANNERS | NCOPTION_INHIBIT_SETLOCALE - | NCOPTION_NO_ALTERNATE_SCREEN | NCOPTION_NO_CLEAR_BITMAPS - | NCOPTION_PRESERVE_CURSOR, + .flags = NCOPTION_SUPPRESS_BANNERS + | NCOPTION_NO_ALTERNATE_SCREEN + | NCOPTION_NO_CLEAR_BITMAPS + | NCOPTION_PRESERVE_CURSOR, }; struct notcurses* nc = notcurses_init(&opts, NULL); if(nc == NULL){ diff --git a/src/lib/kitty.c b/src/lib/kitty.c index 05c63d506..3ff686833 100644 --- a/src/lib/kitty.c +++ b/src/lib/kitty.c @@ -1145,10 +1145,8 @@ int kitty_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f, } if(animated){ fbuf_free(&s->glyph); - s->invalidated = SPRIXEL_LOADED; - }else{ - s->invalidated = SPRIXEL_LOADED; } + s->invalidated = SPRIXEL_LOADED; return ret; } diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index f0702fd37..0a7672a63 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1544,6 +1544,43 @@ void scroll_down(ncplane* n){ } } +int ncplane_scrollup(ncplane* n, int r){ + if(!ncplane_scrolling_p(n)){ + logerror("can't scroll %d on non-scrolling plane\n", r); + return -1; + } + if(r < 0){ + logerror("can't scroll %d lines\n", r); + return -1; + } + while(r-- > 0){ + scroll_down(n); + } + return 0; +} + +// Scroll |n| up until |child| is no longer hidden beneath it. Returns an +// error if |child| is not a child of |n|, or |n| is not scrolling, or |child| +// is fixed. Returns the number of scrolling events otherwise (might be 0). +int ncplane_scrollup_child(ncplane* n, const ncplane* child){ + if(ncplane_parent_const(child) != n){ + logerror("not a child of specified plane\n"); + return -1; + } + if(child->fixedbound){ + logerror("child plane is fixed\n"); + return -1; + } + int parend = ncplane_abs_y(n) + ncplane_dim_y(n); // where parent ends + int chend = ncplane_abs_y(child) + ncplane_dim_y(child); // where child ends + if(chend <= parend){ + return 0; + } + int r = chend - parend; // how many rows we need scroll parent + int ret = ncplane_scrollup(n, r); + return ret; +} + int nccell_width(const ncplane* n __attribute__ ((unused)), const nccell* c){ return nccell_cols(c); } diff --git a/src/lib/render.c b/src/lib/render.c index 60b5bd49d..0c92c11d9 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -840,7 +840,7 @@ clean_sprixels(notcurses* nc, ncpile* p, fbuf* f){ } continue; // don't account as an elision } - if(s->invalidated == SPRIXEL_MOVED || s->invalidated == SPRIXEL_INVALIDATED){ + if(s->invalidated == SPRIXEL_MOVED || s->invalidated == SPRIXEL_INVALIDATED || s->invalidated == SPRIXEL_UNSEEN){ int y, x; ncplane_yx(s->n, &y, &x); //fprintf(stderr, "1 MOVING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); @@ -929,6 +929,7 @@ rasterize_scrolls(ncpile* p, fbuf* f){ if(goto_location(p->nc, f, p->dimy, 0)){ return -1; } + // FIXME if bce is set, we need reset background color while(p->scrolls){ if(fbuf_putc(f, '\n') < 0){ return -1; @@ -955,9 +956,9 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){ while( (s = *parent) ){ //fprintf(stderr, "YARR HARR HARR SPIRXLE %u STATE %d\n", s->id, s->invalidated); if(s->invalidated == SPRIXEL_INVALIDATED){ -//fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); - int y,x; + int y, x; ncplane_yx(s->n, &y, &x); +//fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); int r = sprite_draw(&nc->tcache, p, s, f, y + nc->margin_t, x + nc->margin_l); if(r < 0){ return -1; @@ -966,7 +967,7 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){ nc->rstate.hardcursorpos = true; }else if(s->invalidated == SPRIXEL_LOADED){ if(nc->tcache.pixel_commit){ - int y,x; + int y, x; ncplane_yx(s->n, &y, &x); if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){ return -1; diff --git a/src/lib/sixel.c b/src/lib/sixel.c index b862a4e66..9e588e683 100644 --- a/src/lib/sixel.c +++ b/src/lib/sixel.c @@ -933,7 +933,13 @@ int sixel_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f, } if(s->invalidated == SPRIXEL_MOVED){ for(int yy = s->movedfromy ; yy < s->movedfromy + s->dimy && yy < p->dimy ; ++yy){ + if(yy < 0){ + continue; + } for(int xx = s->movedfromx ; xx < s->movedfromx + s->dimx && xx < p->dimx ; ++xx){ + if(xx < 0){ + continue; + } struct crender *r = &p->crender[yy * p->dimx + xx]; if(!r->sprixel || sprixel_state(r->sprixel, yy, xx) != SPRIXCELL_OPAQUE_SIXEL){ r->s.damaged = 1; diff --git a/src/lib/sprite.c b/src/lib/sprite.c index 78ade957a..f1a3fbad8 100644 --- a/src/lib/sprite.c +++ b/src/lib/sprite.c @@ -67,7 +67,7 @@ sprixel* sprixel_recycle(ncplane* n){ // store the original (absolute) coordinates from which we moved, so that // we can invalidate them in sprite_draw(). void sprixel_movefrom(sprixel* s, int y, int x){ - if(s->invalidated != SPRIXEL_HIDE){ + if(s->invalidated != SPRIXEL_HIDE && s->invalidated != SPRIXEL_UNSEEN){ if(s->invalidated != SPRIXEL_MOVED){ // FIXME if we're Sixel, we need to effect any wipes that were run // (we normally don't because redisplaying sixel doesn't change @@ -168,7 +168,7 @@ int sprixel_load(sprixel* spx, fbuf* f, int pixy, int pixx, fbuf_free(&spx->glyph); memcpy(&spx->glyph, f, sizeof(*f)); } - spx->invalidated = SPRIXEL_INVALIDATED; + spx->invalidated = SPRIXEL_UNSEEN; spx->pixx = pixx; spx->pixy = pixy; spx->parse_start = parse_start; diff --git a/src/lib/sprite.h b/src/lib/sprite.h index 5b5140fa7..9312f73ab 100644 --- a/src/lib/sprite.h +++ b/src/lib/sprite.h @@ -17,8 +17,9 @@ struct blitterargs; typedef enum { SPRIXEL_QUIESCENT, // up-to-date and visible at the proper place + SPRIXEL_UNSEEN, // not yet loaded, invisible, but wants loading SPRIXEL_LOADED, // loaded, but not yet made visible (kitty-only) - SPRIXEL_INVALIDATED, // not up-to-date, need reload, trumps MOVED + SPRIXEL_INVALIDATED, // not up-to-date, need reload SPRIXEL_HIDE, // queued for destruction SPRIXEL_MOVED, // visible, up-to-date, but in the wrong place } sprixel_e; diff --git a/src/poc/ncwidth.c b/src/poc/ncwidth.c index ad398ce6b..12c87dcd3 100644 --- a/src/poc/ncwidth.c +++ b/src/poc/ncwidth.c @@ -4,6 +4,7 @@ #include #include #include +#include #include static int