From 7ca5a7e82c2291d890457c278110702fa0be1291 Mon Sep 17 00:00:00 2001 From: nick black Date: Wed, 17 Jun 2020 19:39:37 -0400 Subject: [PATCH] ncneofetch: create info plane #550 --- NEWS.md | 3 + USAGE.md | 5 ++ include/notcurses/notcurses.h | 104 ++++++++++++++++++++++++++-------- src/fetch/main.c | 95 +++++++++++++++++++++---------- tests/fade.cpp | 12 ++-- 5 files changed, 159 insertions(+), 60 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2366ecc1e..9b000f3ab 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,10 @@ This document attempts to list user-visible changes and any major internal rearrangements of Notcurses. * 1.5.2 (not yet released) + * The `ncneofetch` program has been added, of no great consequence. * A `NULL` value can now be passed as `sbytes` to `ncplane_puttext()`. + * `ncdirect_render_image()` has been added, allowing images to be + rendered in direct mode. * 1.5.1 (2020-07-15) * The semantics of rendering have changed slightly. In 1.5.0 and prior diff --git a/USAGE.md b/USAGE.md index 2534f3f5e..9a5a035cd 100644 --- a/USAGE.md +++ b/USAGE.md @@ -320,6 +320,11 @@ int ncdirect_cursor_up(struct ncdirect* nc, int num); int ncdirect_cursor_left(struct ncdirect* nc, int num); int ncdirect_cursor_right(struct ncdirect* nc, int num); int ncdirect_cursor_down(struct ncdirect* nc, int num); + +// Display an image using the specified blitter and scaling. The image may +// be arbitrarily many rows -- the output will scroll -- but will only occupy +// the column of the cursor, and those to the right. +nc_err_e ncdirect_render_image(const char* filename, ncblitter_e blitter, ncscale_e scale); ``` ## Alignment diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index acf4435b0..2a12b3966 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -41,6 +41,31 @@ struct ncmultiselector; // widget supporting selecting 0..n from n options struct ncreader; // widget supporting free string input ala readline struct ncfadectx; // context for a palette fade operation +// each has the empty cell in addition to the product of its dimensions. i.e. +// NCBLIT_1x1 has two states: empty and full block. NCBLIT_1x1x4 has five +// states: empty, the three shaded blocks, and the full block. +typedef enum { + NCBLIT_DEFAULT, // let the ncvisual pick + NCBLIT_1x1, // full block █ + NCBLIT_2x1, // full/(upper|left) blocks ▄█ + NCBLIT_1x1x4, // shaded full blocks ▓▒░█ + NCBLIT_2x2, // quadrants ▗▐ ▖▄▟▌▙█ + NCBLIT_4x1, // four vert/horz levels █▆▄▂ / ▎▌▊█ + NCBLIT_BRAILLE, // 4 rows, 2 cols (braille) ⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿ + NCBLIT_8x1, // eight vert/horz levels █▇▆▅▄▃▂▁ / ▏▎▍▌▋▊▉█ + NCBLIT_SIXEL, // 6 rows, 1 col (RGB), spotty support among terminals +} ncblitter_e; + +// How to scale an ncvisual during rendering. NCSCALE_NONE will apply no +// scaling. NCSCALE_SCALE scales a visual to the plane's size, maintaining +// aspect ratio. NCSCALE_STRETCH stretches and scales the image in an +// attempt to fill the entirety of the plane. +typedef enum { + NCSCALE_NONE, + NCSCALE_SCALE, + NCSCALE_STRETCH, +} ncscale_e; + // Initialize a direct-mode notcurses context on the connected terminal at 'fp'. // 'fp' must be a tty. You'll usually want stdout. Direct mode supportes a // limited subset of notcurses routines which directly affect 'fp', and neither @@ -102,6 +127,12 @@ API int ncdirect_cursor_yx(struct ncdirect* n, int* y, int* x); API int ncdirect_cursor_push(struct ncdirect* n); API int ncdirect_cursor_pop(struct ncdirect* n); +// Display an image using the specified blitter and scaling. The image may +// // be arbitrarily many rows -- the output will scroll -- but will only occupy +// // the column of the cursor, and those to the right. +API nc_err_e ncdirect_render_image(const char* filename, ncblitter_e blitter, + ncscale_e scale); + // Clear the screen. API int ncdirect_clear(struct ncdirect* nc); @@ -898,16 +929,6 @@ typedef struct notcurses_options { // there can be four numbers separated by commas. API int notcurses_lex_margins(const char* op, notcurses_options* opts); -// How to scale a visual in ncvisual_decode(). NCSCALE_NONE will apply no -// scaling. NCSCALE_SCALE scales a visual to the plane's size, maintaining -// aspect ratio. NCSCALE_STRETCH stretches and scales the image in an attempt -// to fill the entirety of the plane. -typedef enum { - NCSCALE_NONE, - NCSCALE_SCALE, - NCSCALE_STRETCH, -} ncscale_e; - // Lex a visual scaling mode (one of "none", "stretch", or "scale"). API int notcurses_lex_scalemode(const char* op, ncscale_e* scalemode); @@ -2170,6 +2191,30 @@ ncplane_rounded_box(struct ncplane* n, uint32_t attr, uint64_t channels, return ret; } +static inline int +ncplane_perimeter_rounded(struct ncplane* n, uint32_t attrword, + uint64_t channels, unsigned ctlword){ + if(ncplane_cursor_move_yx(n, 0, 0)){ + return -1; + } + int dimy, dimx; + ncplane_dim_yx(n, &dimy, &dimx); + cell ul = CELL_TRIVIAL_INITIALIZER; + cell ur = CELL_TRIVIAL_INITIALIZER; + cell ll = CELL_TRIVIAL_INITIALIZER; + cell lr = CELL_TRIVIAL_INITIALIZER; + cell vl = CELL_TRIVIAL_INITIALIZER; + cell hl = CELL_TRIVIAL_INITIALIZER; + if(cells_rounded_box(n, attrword, channels, &ul, &ur, &ll, &lr, &hl, &vl)){ + return -1; + } + int r = ncplane_box_sized(n, &ul, &ur, &ll, &lr, &hl, &vl, dimy, dimx, ctlword); + cell_release(n, &ul); cell_release(n, &ur); + cell_release(n, &ll); cell_release(n, &lr); + cell_release(n, &hl); cell_release(n, &vl); + return r; +} + static inline int ncplane_rounded_box_sized(struct ncplane* n, uint32_t attr, uint64_t channels, int ylen, int xlen, unsigned ctlword){ @@ -2199,6 +2244,30 @@ ncplane_double_box(struct ncplane* n, uint32_t attr, uint64_t channels, return ret; } +static inline int +ncplane_perimeter_double(struct ncplane* n, uint32_t attrword, + uint64_t channels, unsigned ctlword){ + if(ncplane_cursor_move_yx(n, 0, 0)){ + return -1; + } + int dimy, dimx; + ncplane_dim_yx(n, &dimy, &dimx); + cell ul = CELL_TRIVIAL_INITIALIZER; + cell ur = CELL_TRIVIAL_INITIALIZER; + cell ll = CELL_TRIVIAL_INITIALIZER; + cell lr = CELL_TRIVIAL_INITIALIZER; + cell vl = CELL_TRIVIAL_INITIALIZER; + cell hl = CELL_TRIVIAL_INITIALIZER; + if(cells_double_box(n, attrword, channels, &ul, &ur, &ll, &lr, &hl, &vl)){ + return -1; + } + int r = ncplane_box_sized(n, &ul, &ur, &ll, &lr, &hl, &vl, dimy, dimx, ctlword); + cell_release(n, &ul); cell_release(n, &ur); + cell_release(n, &ll); cell_release(n, &lr); + cell_release(n, &hl); cell_release(n, &vl); + return r; +} + static inline int ncplane_double_box_sized(struct ncplane* n, uint32_t attr, uint64_t channels, int ylen, int xlen, unsigned ctlword){ @@ -2225,21 +2294,6 @@ API struct ncvisual* ncvisual_from_rgba(const void* rgba, int rows, API struct ncvisual* ncvisual_from_bgra(const void* rgba, int rows, int rowstride, int cols); -// each has the empty cell in addition to the product of its dimensions. i.e. -// NCBLIT_1x1 has two states: empty and full block. NCBLIT_1x1x4 has five -// states: empty, the three shaded blocks, and the full block. -typedef enum { - NCBLIT_DEFAULT, // let the ncvisual pick - NCBLIT_1x1, // full block █ - NCBLIT_2x1, // full/(upper|left) blocks ▄█ - NCBLIT_1x1x4, // shaded full blocks ▓▒░█ - NCBLIT_2x2, // quadrants ▗▐ ▖▄▟▌▙█ - NCBLIT_4x1, // four vert/horz levels █▆▄▂ / ▎▌▊█ - NCBLIT_BRAILLE, // 4 rows, 2 cols (braille) ⡀⡄⡆⡇⢀⣀⣄⣆⣇⢠⣠⣤⣦⣧⢰⣰⣴⣶⣷⢸⣸⣼⣾⣿ - NCBLIT_8x1, // eight vert/horz levels █▇▆▅▄▃▂▁ / ▏▎▍▌▋▊▉█ - NCBLIT_SIXEL, // 6 rows, 1 col (RGB), spotty support among terminals -} ncblitter_e; - // Promote an ncplane 'n' to an ncvisual. The plane may contain only spaces, // half blocks, and full blocks. The latter will be checked, and any other // glyph will result in a NULL being returned. This function exists so that diff --git a/src/fetch/main.c b/src/fetch/main.c index 3810657c2..96d381df8 100644 --- a/src/fetch/main.c +++ b/src/fetch/main.c @@ -67,34 +67,13 @@ done: return dinfo; } -static int -linux_ncneofetch(struct notcurses* nc){ +static const distro_info* +linux_ncneofetch(void){ const distro_info* dinfo = getdistro(); if(dinfo == NULL){ - return -1; + return NULL; } - nc_err_e err; - struct ncvisual* ncv = ncvisual_from_file(dinfo->logofile, &err); - if(ncv == NULL){ - fprintf(stderr, "Error opening logo file at %s\n", dinfo->logofile); - return -1; - } - struct ncvisual_options vopts = { - .scaling = NCSCALE_SCALE, - .blitter = NCBLIT_2x2, - }; - struct ncplane* n = ncvisual_render(nc, ncv, &vopts); - if(n == NULL){ - ncvisual_destroy(ncv); - return -1; - } - if(notcurses_render(nc)){ - ncvisual_destroy(ncv); - return -1; - } - ncplane_destroy(n); - ncvisual_destroy(ncv); - return 0; + return dinfo; } typedef enum { @@ -119,19 +98,77 @@ get_kernel(void){ return NCNEO_UNKNOWN; } +static struct ncplane* +display(struct notcurses* nc, const distro_info* dinfo){ + if(dinfo->logofile){ + nc_err_e err; + struct ncvisual* ncv = ncvisual_from_file(dinfo->logofile, &err); + if(ncv == NULL){ + fprintf(stderr, "Error opening logo file at %s\n", dinfo->logofile); + return NULL; + } + struct ncvisual_options vopts = { + .scaling = NCSCALE_SCALE, + .blitter = NCBLIT_2x2, + .n = notcurses_stdplane(nc), + }; + if(ncvisual_render(nc, ncv, &vopts) == NULL){ + ncvisual_destroy(ncv); + return NULL; + } + ncvisual_destroy(ncv); + } + return 0; +} + +static const distro_info* +freebsd_ncneofetch(void){ + static const distro_info fbsd = { + .name = "FreeBSD", + .logofile = NULL, // FIXME + }; + return &fbsd; +} + +static int +infoplane(struct notcurses* nc){ + struct ncplane* infop = ncplane_new(nc, 8, 60, 0, 0, NULL); + if(infop == NULL){ + return -1; + } + if(ncplane_perimeter_rounded(infop, 0, 0, 0)){ + return -1; + } + return 0; +} + static int ncneofetch(struct notcurses* nc){ + const distro_info* dinfo = NULL; ncneo_kernel_e kern = get_kernel(); switch(kern){ case NCNEO_LINUX: - return linux_ncneofetch(nc); + dinfo = linux_ncneofetch(); + break; case NCNEO_FREEBSD: - // FIXME + dinfo = freebsd_ncneofetch(); break; case NCNEO_UNKNOWN: - return -1; + break; } - return -1; + if(dinfo == NULL){ + return -1; + } + if(display(nc, dinfo)){ + return -1; // FIXME soldier on, perhaps? + } + if(infoplane(nc)){ + return -1; + } + if(notcurses_render(nc)){ + return -1; + } + return 0; } int main(void){ diff --git a/tests/fade.cpp b/tests/fade.cpp index ce4ffa37b..09d3dc397 100644 --- a/tests/fade.cpp +++ b/tests/fade.cpp @@ -12,7 +12,7 @@ auto pulser(struct notcurses* nc, struct ncplane* ncp __attribute__ ((unused)), struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); auto delta = timespec_to_ns(&now) - timespec_to_ns(pulsestart); - if(delta > 500000000){ + if(delta > 250000000){ return 1; } return 0; @@ -62,14 +62,14 @@ TEST_CASE("Fade") { CHECK(0 == notcurses_render(nc_)); struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 500000000; + ts.tv_nsec = 250000000; CHECK(0 == ncplane_fadeout(n_, &ts, nullptr, nullptr)); } SUBCASE("FadeIn") { struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 500000000; + ts.tv_nsec = 250000000; CHECK(0 == ncplane_fadein(n_, &ts, nullptr, nullptr)); } @@ -77,21 +77,21 @@ TEST_CASE("Fade") { CHECK(0 == notcurses_render(nc_)); struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 500000000; + ts.tv_nsec = 250000000; CHECK(0 < ncplane_fadeout(n_, &ts, fadeaborter, nullptr)); } SUBCASE("FadeInAbort") { struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 500000000; + ts.tv_nsec = 250000000; CHECK(0 < ncplane_fadein(n_, &ts, fadeaborter, nullptr)); } SUBCASE("Pulse") { struct timespec ts; ts.tv_sec = 0; - ts.tv_nsec = 150000000; + ts.tv_nsec = 75000000; ncplane_erase(n_); ncplane_set_fg(n_, 0xffd700); CHECK(0 < ncplane_printf_aligned(n_, dimy - 1, NCALIGN_CENTER, "pulllllllse"));