diff --git a/NEWS.md b/NEWS.md index e6861c9bf..914612dbf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,8 @@ rearrangements of Notcurses. `NCOPTION_SCROLLING`, `NCOPTION_NO_CLEAR_BITMAPS`, `NCOPTION_NO_ALTERNATE_SCREEN`, and `NCOPTION_PRESERVE_CURSOR`. * Added `ncvisual_from_sixel()`. + * The control sequence corresponding to a pixel-blitted `ncvisual()` + can be retrieved by using `ncplane_at_yx()` on the sprixel plane. * 3.0.1 (2021-12-14) * Added the `NCPLANE_OPTION_VSCROLL` flag. Creating an `ncplane` with this diff --git a/doc/man/man3/notcurses_plane.3.md b/doc/man/man3/notcurses_plane.3.md index 2bf79e6f5..b99194a73 100644 --- a/doc/man/man3/notcurses_plane.3.md +++ b/doc/man/man3/notcurses_plane.3.md @@ -375,6 +375,10 @@ secondary column of a wide glyph with **ncplane_at_yx_cell** will fill in the **nccell** argument such that **nccell_extended_gcluster(3)** returns an empty string, and **nccell_wide_right_p(3)** returns **true**. +If **ncplane_at_yx** is invoked upon a sprixel plane, the control sequence will +be returned for any valid coordinates (note that this may be quite large). +This does not apply to **ncplane_at_yx_cell**, which will return an error. + **ncplane_set_name** sets the plane's name, freeing any old name. ***name*** may be **NULL**. **ncplane_set_name** duplicates the provided name internally. diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 7a4084faf..ad217871e 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -1876,18 +1876,22 @@ API int ncplane_scrollup_child(struct ncplane* n, const struct ncplane* child) // characters, spaces, half blocks, and full blocks. The plane must have // an even number of columns. Use the ncvisual rotation for a more // flexible approach. -API int ncplane_rotate_cw(struct ncplane* n); -API int ncplane_rotate_ccw(struct ncplane* n); +API int ncplane_rotate_cw(struct ncplane* n) + __attribute__ ((nonnull (1))); +API int ncplane_rotate_ccw(struct ncplane* n) + __attribute__ ((nonnull (1))); // Retrieve the current contents of the cell under the cursor. The EGC is // returned, or NULL on error. This EGC must be free()d by the caller. The // stylemask and channels are written to 'stylemask' and 'channels', respectively. -API char* ncplane_at_cursor(struct ncplane* n, uint16_t* stylemask, uint64_t* channels); +API char* ncplane_at_cursor(struct ncplane* n, uint16_t* stylemask, uint64_t* channels) + __attribute__ ((nonnull (1))); // Retrieve the current contents of the cell under the cursor into 'c'. This // cell is invalidated if the associated plane is destroyed. Returns the number // of bytes in the EGC, or -1 on error. -API int ncplane_at_cursor_cell(struct ncplane* n, nccell* c); +API int ncplane_at_cursor_cell(struct ncplane* n, nccell* c) + __attribute__ ((nonnull (1, 2))); // Retrieve the current contents of the specified cell. The EGC is returned, or // NULL on error. This EGC must be free()d by the caller. The stylemask and @@ -1895,16 +1899,20 @@ API int ncplane_at_cursor_cell(struct ncplane* n, nccell* c); // represents how the cell will be used during rendering, and thus integrates // any base cell where appropriate. If called upon the secondary columns of a // wide glyph, the EGC will be returned (i.e. this function does not distinguish -// between the primary and secondary columns of a wide glyph). +// between the primary and secondary columns of a wide glyph). If called on a +// sprixel plane, its control sequence is returned for all valid locations. API char* ncplane_at_yx(const struct ncplane* n, int y, int x, - uint16_t* stylemask, uint64_t* channels); + uint16_t* stylemask, uint64_t* channels) + __attribute__ ((nonnull (1))); // Retrieve the current contents of the specified cell into 'c'. This cell is // invalidated if the associated plane is destroyed. Returns the number of // bytes in the EGC, or -1 on error. Unlike ncplane_at_yx(), when called upon // the secondary columns of a wide glyph, the return can be distinguished from -// the primary column (nccell_wide_right_p(c) will return true). -API int ncplane_at_yx_cell(struct ncplane* n, int y, int x, nccell* c); +// the primary column (nccell_wide_right_p(c) will return true). It is an +// error to call this on a sprixel plane (unlike ncplane_at_yx()). +API int ncplane_at_yx_cell(struct ncplane* n, int y, int x, nccell* c) + __attribute__ ((nonnull (1, 4))); // Create a flat string from the EGCs of the selected region of the ncplane // 'n'. Start at the plane's 'begy'x'begx' coordinate (which must lie on the @@ -1912,20 +1920,24 @@ API int ncplane_at_yx_cell(struct ncplane* n, int y, int x, nccell* c); // 'lenx' can be specified as 0 to go through the boundary of the plane. // -1 can be specified for 'begx'/'begy' to use the current cursor location. API char* ncplane_contents(struct ncplane* n, int begy, int begx, - unsigned leny, unsigned lenx); + unsigned leny, unsigned lenx) + __attribute__ ((nonnull (1))); // Manipulate the opaque user pointer associated with this plane. // ncplane_set_userptr() returns the previous userptr after replacing // it with 'opaque'. the others simply return the userptr. -API void* ncplane_set_userptr(struct ncplane* n, void* opaque); -API void* ncplane_userptr(struct ncplane* n); +API void* ncplane_set_userptr(struct ncplane* n, void* opaque) + __attribute__ ((nonnull (1))); +API void* ncplane_userptr(struct ncplane* n) + __attribute__ ((nonnull (1))); // Find the center coordinate of a plane, preferring the top/left in the // case of an even number of rows/columns (in such a case, there will be one // more cell to the bottom/right of the center than the top/left). The // center is then modified relative to the plane's origin. API void ncplane_center_abs(const struct ncplane* n, int* RESTRICT y, - int* RESTRICT x); + int* RESTRICT x) + __attribute__ ((nonnull (1))); // Create an RGBA flat array from the selected region of the ncplane 'nc'. // Start at the plane's 'begy'x'begx' coordinate (which must lie on the diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 3706b3849..e0e898ab9 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -229,6 +229,15 @@ char* ncplane_at_yx(const ncplane* n, int y, int x, uint16_t* stylemask, uint64_ logerror("invalid coordinates: %d/%d", y, x); return NULL; } + if(n->sprite){ + if(stylemask){ + *stylemask = 0; + } + if(channels){ + *channels = 0; + } + return strdup(n->sprite->glyph.buf); + } const nccell* yx = &n->fb[nfbcellidx(n, y, x)]; // if we're the right side of a wide glyph, we return the main glyph if(nccell_wide_right_p(yx)){ @@ -258,6 +267,10 @@ int ncplane_at_cursor_cell(ncplane* n, nccell* c){ } int ncplane_at_yx_cell(ncplane* n, int y, int x, nccell* c){ + if(n->sprite){ + logerror("invoked on a sprixel plane"); + return -1; + } if(y < 0){ if(y != -1){ logerror("invalid y: %d", y); diff --git a/src/poc/quantanal.c b/src/poc/quantanal.c index 4780ab2fa..3a4825f9e 100644 --- a/src/poc/quantanal.c +++ b/src/poc/quantanal.c @@ -1,8 +1,10 @@ +#include #include +#include int main(int argc, char** argv){ if(argc < 2){ - fprintf(stderr, "usage: %s images...\n", *argv); + fprintf(stderr, "usage: %s images..." NL, *argv); return EXIT_FAILURE; } struct notcurses_options opts = {0}; @@ -20,7 +22,7 @@ int main(int argc, char** argv){ struct ncvisual* ncv = ncvisual_from_file(*argv); if(ncv == NULL){ notcurses_stop(nc); - fprintf(stderr, "error opening %s\n", *argv); + fprintf(stderr, "error opening %s" NL, *argv); return EXIT_FAILURE; } struct ncplane* ncp = ncplane_dup(stdn, NULL); @@ -34,16 +36,25 @@ int main(int argc, char** argv){ vopts.flags = NCVISUAL_OPTION_NODEGRADE; if(ncvisual_blit(nc, ncv, &vopts) == NULL){ notcurses_stop(nc); - fprintf(stderr, "error rendering %s\n", *argv); + fprintf(stderr, "error rendering %s" NL, *argv); return EXIT_FAILURE; } - // FIXME acquire sixel as s - char* s = strdup(""); + char* s; + if((s = ncplane_at_yx(ncp, 0, 0, NULL, NULL)) == NULL){ + notcurses_stop(nc); + fprintf(stderr, "error retrieving sixel for %s" NL, *argv); + return EXIT_FAILURE; + } + ncplane_set_fg_rgb(stdn, 0x74b72e); + size_t slen = strlen(s); + ncplane_printf(stdn, " control sequence: %" PRIuPTR " byte%s\n", + slen, slen == 1 ? "" : "s"); + notcurses_render(nc); unsigned leny = 0, lenx = 0; // FIXME struct ncvisual* quantncv = ncvisual_from_sixel(s, leny, lenx); if(quantncv == NULL){ notcurses_stop(nc); - fprintf(stderr, "error loading %zuB sixel\n", strlen(s)); + fprintf(stderr, "error loading %" PRIuPTR "B sixel" NL, slen); free(s); return EXIT_FAILURE; }