mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-09 17:19:03 -04:00
require explicit check for pixel support
Add `notcurses_check_pixel_support()` and `ncdirect_check_pixel_support()` per #1367. Removes NCOPTION_VERIFY_SIXEL, again per #1367. Adds `free_terminfo_cache()`, and calls it from both `notcurses_stop_minimal()` and `ncdirect_stop()`. Update all documentation. Closes #1371 and #1367.
This commit is contained in:
parent
4533d42fa0
commit
6c7c9be6d2
8
NEWS.md
8
NEWS.md
@ -3,11 +3,15 @@ rearrangements of Notcurses.
|
||||
|
||||
* 2.2.3 (not yet released)
|
||||
* Add `SIGILL` to the set of fatal signals we handle.
|
||||
* Added `NCKEY_SIGNAL`. `NCKEY_RESIZE` is now an alias for `NCKEY_SIGNAL`.
|
||||
* `SIGCONT` now synthesizes a `NCKEY_SIGNAL`.
|
||||
* Add the `nctree` widget for line-oriented hierarchical data. See
|
||||
the new `notcurses_tree(3)` man page for complete information.
|
||||
* Implemented `NCBLIT_PIXEL` for terminals reporting Sixel support.
|
||||
Attempt Sixel detection iff `NCOPTION_VERIFY_SIXEL` is provided to
|
||||
`notcurses_init()`. Sixel detection can delay or even block initialization.
|
||||
Added `notcurses_check_pixel_support()` and its companion
|
||||
`ncdirect_check_pixel_support()`, which must be called (and must return
|
||||
success) before `NCBLIT_PIXEL` will be available. `NCBLIT_PIXEL` degrades
|
||||
to `NCBLIT_3x2` until support is verified.
|
||||
|
||||
* 2.2.2 (2021-02-18):
|
||||
* `notcurses_stats()` no longer qualifies its `notcurses*` argument with
|
||||
|
16
USAGE.md
16
USAGE.md
@ -91,11 +91,12 @@ typedef enum {
|
||||
// doing something weird (setting a locale not based on LANG).
|
||||
#define NCOPTION_INHIBIT_SETLOCALE 0x0001
|
||||
|
||||
// Checking for Sixel support requires writing an escape, and then reading an
|
||||
// inline reply from the terminal. Since this can interact poorly with actual
|
||||
// user input, it's not done unless Sixel will actually be used. Set this flag
|
||||
// to unconditionally test for Sixel support in notcurses_init().
|
||||
#define NCOPTION_VERIFY_SIXEL 0x0002
|
||||
// Checking for pixel support might require writing a control sequence, and
|
||||
// then reading a reply directly from the terminal. If the terminal doesn't
|
||||
// support this, the application will lock up. If you'll be using pixels, set
|
||||
// this flag to perform the check in notcurses_init(). You must otherwise call
|
||||
// notcurses_check_pixel() before NCBLIT_PIXEL will become available.
|
||||
#define NCOPTION_VERIFY_PIXEL 0x0002ull
|
||||
|
||||
// We typically install a signal handler for SIGWINCH that generates a resize
|
||||
// event in the notcurses_getc() queue. Set to inhibit this handler.
|
||||
@ -306,6 +307,11 @@ bool notcurses_cansextants(const struct notcurses* nc);
|
||||
|
||||
// Can we draw Braille? The Linux console cannot.
|
||||
bool notcurses_canbraille(const struct notcurses* nc);
|
||||
|
||||
// If NCOPTION_VERIFY_PIXEL was not supplied to notcurses_init(), this
|
||||
// function must successfully return before NCBLIT_PIXEL is available. Returns
|
||||
// -1 on error, 0 if pixel mode is not supported, or 1 if it is supported.
|
||||
int notcurses_check_pixel(struct notcurses* nc);
|
||||
```
|
||||
|
||||
## Direct mode
|
||||
|
@ -72,6 +72,8 @@ notcurses_direct - minimal notcurses instances for styling text
|
||||
|
||||
**bool ncdirect_canutf8(const struct ncdirect* ***n***);**
|
||||
|
||||
**int ncdirect_check_pixel_support(struct ncdirect* ***n***);**
|
||||
|
||||
**int ncdirect_hline_interp(struct ncdirect* ***n***, const char* ***egc***, int ***len***, uint64_t ***h1***, uint64_t ***h2***);**
|
||||
|
||||
**int ncdirect_vline_interp(struct ncdirect* ***n***, const char* ***egc***, int ***len***, uint64_t ***h1***, uint64_t ***h2***);**
|
||||
@ -144,6 +146,10 @@ information, consult **readline(3)**. If you want input echoed to the
|
||||
terminal while using **ncdirect_readline**, **NCDIRECT_OPTION_INHIBIT_CBREAK**
|
||||
must be supplied to **ncdirect_init**.
|
||||
|
||||
**ncdirect_check_pixel_support** must be called (and successfully return)
|
||||
before **NCBLIT_PIXEL** can be used to render images; see
|
||||
**notcurses_visual(3)** for more details.
|
||||
|
||||
# RETURN VALUES
|
||||
|
||||
**ncdirect_init** returns **NULL** on failure. Otherwise, the return value
|
||||
@ -153,6 +159,9 @@ to **ncdirect_stop**.
|
||||
**ncdirect_putstr** and **ncdirect_printf_aligned** return the number of bytes
|
||||
written on success. On failure, they return some negative number.
|
||||
|
||||
**ncdirect_check_pixel_support** returns -1 on error, 0 if there is no pixel
|
||||
support, and 1 if pixel support is successfully detected.
|
||||
|
||||
All other functions return 0 on success, and non-zero on error.
|
||||
|
||||
# SEE ALSO
|
||||
@ -161,5 +170,6 @@ All other functions return 0 on success, and non-zero on error.
|
||||
**readline(3)**
|
||||
**notcurses(3)**,
|
||||
**notcurses_plane(3)**,
|
||||
**notcurses_visual(3)**,
|
||||
**terminfo(5)**,
|
||||
**termios(3)**
|
||||
|
@ -12,7 +12,6 @@ notcurses_init - initialize a notcurses instance
|
||||
|
||||
```c
|
||||
#define NCOPTION_INHIBIT_SETLOCALE 0x0001ull
|
||||
#define NCOPTION_VERIFY_SIXEL 0x0002ull
|
||||
#define NCOPTION_NO_WINCH_SIGHANDLER 0x0004ull
|
||||
#define NCOPTION_NO_QUIT_SIGHANDLERS 0x0008ull
|
||||
#define NCOPTION_SUPPRESS_BANNERS 0x0020ull
|
||||
@ -111,12 +110,6 @@ zero. The following flags are defined:
|
||||
the **LANG** environment variable. Your program should call **setlocale(3)**
|
||||
itself, usually as one of the first lines.
|
||||
|
||||
* **NCOPTION_VERIFY_SIXEL**: Checking for Sixel support requires writing an
|
||||
escape, and then reading an inline reply from the terminal. Since this can
|
||||
interact poorly with actual user input, it's not done unless Sixel will
|
||||
actually be used. Set this flag to unconditionally test for Sixel support
|
||||
in **notcurses_init**.
|
||||
|
||||
* **NCOPTION_NO_WINCH_SIGHANDLER**: A signal handler will usually be installed
|
||||
for **SIGWINCH**, resulting in **NCKEY_RESIZE** events being generated on
|
||||
input. With this flag, the handler will not be installed.
|
||||
|
@ -92,6 +92,8 @@ typedef int (*streamcb)(struct notcurses*, struct ncvisual*, void*);
|
||||
|
||||
**int ncplane_qrcode(struct ncplane* ***n***, int* ***ymax***, int* ***xmax***, const void* ***data***, size_t ***len***)**
|
||||
|
||||
**int notcurses_check_pixel_support(struct notcurses* ***nc***);**
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
An **ncvisual** is a virtual pixel framebuffer. They can be created from
|
||||
@ -203,6 +205,16 @@ Finally, rendering operates slightly differently when two planes have both been
|
||||
blitted, and one lies atop the other. See **notcurses_render(3)** for more
|
||||
information.
|
||||
|
||||
# PIXEL BLITTING
|
||||
|
||||
Some terminals support pixel-based output, either via Sixel or some bespoke
|
||||
mechanism. Checking for Sixel support requires interrogating the terminal and
|
||||
reading a response. This takes time, and will never complete if the terminal
|
||||
doesn't respond. Before **NCBLIT_PIXEL** can be used, it is thus necessary to
|
||||
check for support with **notcurses_check_pixel_support**. If this function has
|
||||
not successfully returned, attempts to use **NCBLIT_PIXEL** will fall back to
|
||||
**NCBLIT_3x2** (or fail, if **NCVISUAL_OPTION_NODEGRADE** is used).
|
||||
|
||||
# RETURN VALUES
|
||||
|
||||
**ncvisual_from_file** returns an **ncvisual** object on success, or **NULL**
|
||||
|
@ -217,6 +217,11 @@ namespace ncpp
|
||||
return ncdirect_canutf8 (direct);
|
||||
}
|
||||
|
||||
int check_pixel_support() noexcept
|
||||
{
|
||||
return ncdirect_check_pixel_support (direct);
|
||||
}
|
||||
|
||||
private:
|
||||
ncdirect *direct;
|
||||
};
|
||||
|
@ -216,6 +216,11 @@ API bool ncdirect_canopen_images(const struct ncdirect* n);
|
||||
// Is our encoding UTF-8? Requires LANG being set to a UTF8 locale.
|
||||
API bool ncdirect_canutf8(const struct ncdirect* n);
|
||||
|
||||
// This function must successfully return before NCBLIT_PIXEL is available.
|
||||
// Returns -1 on error, 0 for no support, or 1 if pixel output is supported.
|
||||
// Must not be called concurrently with either input or rasterization.
|
||||
API int ncdirect_check_pixel_support(struct ncdirect* n);
|
||||
|
||||
// Draw horizontal/vertical lines using the specified channels, interpolating
|
||||
// between them as we go. The EGC may not use more than one column. For a
|
||||
// horizontal line, |len| cannot exceed the screen width minus the cursor's
|
||||
|
@ -9,7 +9,7 @@ extern "C" {
|
||||
|
||||
// Special composed key definitions. These values are added to 0x100000.
|
||||
#define NCKEY_INVALID suppuabize(0)
|
||||
#define NCKEY_SIGNAL suppuabize(1) // generated internally in response to SIGWINCH / SIGCONT
|
||||
#define NCKEY_SIGNAL suppuabize(1) // we received either SIGWINCH or SIGCONT
|
||||
#define NCKEY_UP suppuabize(2)
|
||||
#define NCKEY_RIGHT suppuabize(3)
|
||||
#define NCKEY_DOWN suppuabize(4)
|
||||
|
@ -826,11 +826,7 @@ typedef enum {
|
||||
// doing something weird (setting a locale not based on LANG).
|
||||
#define NCOPTION_INHIBIT_SETLOCALE 0x0001ull
|
||||
|
||||
// Checking for Sixel support requires writing an escape, and then reading an
|
||||
// inline reply from the terminal. Since this can interact poorly with actual
|
||||
// user input, it's not done unless Sixel will actually be used. Set this flag
|
||||
// to unconditionally test for Sixel support in notcurses_init().
|
||||
#define NCOPTION_VERIFY_SIXEL 0x0002ull
|
||||
// NCOPTION_VERIFY_PIXEL was removed in 2.2.3. It ought be repurposed. FIXME.
|
||||
|
||||
// We typically install a signal handler for SIGWINCH that generates a resize
|
||||
// event in the notcurses_getc() queue. Set to inhibit this handler.
|
||||
@ -1237,6 +1233,11 @@ API bool notcurses_canbraille(const struct notcurses* nc);
|
||||
// Can we blit to pixel graphics?
|
||||
API bool notcurses_canpixel(const struct notcurses* nc);
|
||||
|
||||
// This function must successfully return before NCBLIT_PIXEL is available.
|
||||
// Returns -1 on error, 0 for no support, or 1 if pixel output is supported.
|
||||
// Must not be called concurrently with either input or rasterization.
|
||||
API int notcurses_check_pixel_support(struct notcurses* nc);
|
||||
|
||||
typedef struct ncstats {
|
||||
// purely increasing stats
|
||||
uint64_t renders; // successful ncpile_render() runs
|
||||
|
@ -637,6 +637,7 @@ ncdirect_stop_minimal(void* vnc){
|
||||
ret |= close(nc->ctermfd);
|
||||
}
|
||||
ret |= ncdirect_flush(nc);
|
||||
free_terminfo_cache(&nc->tcache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1102,3 +1103,13 @@ int ncdirect_flush(const ncdirect* nc){
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncdirect_check_pixel_support(ncdirect* n){
|
||||
if(query_term(&n->tcache, n->ctermfd)){
|
||||
return -1;
|
||||
}
|
||||
if(n->tcache.pixelon){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -286,11 +286,12 @@ typedef struct tinfo {
|
||||
// background_opaque is in use. detect this, and avoid the default if so.
|
||||
// bg_collides_default is either 0x0000000 or 0x1RRGGBB.
|
||||
uint32_t bg_collides_default;
|
||||
bool sextants; // do we have (good, vetted) Unicode 13 sextant support?
|
||||
bool braille; // do we have Braille support? (linux console does not)
|
||||
pthread_mutex_t pixel_query; // only query for pixel support once
|
||||
char* pixelon; // enter pixel graphics mode
|
||||
char* pixeloff; // leave pixel graphics mode
|
||||
bool sixel; // do we have Sixel support?
|
||||
bool pixel_query_done; // have we yet performed pixel query?
|
||||
bool sextants; // do we have (good, vetted) Unicode 13 sextant support?
|
||||
bool braille; // do we have Braille support? (linux console does not)
|
||||
} tinfo;
|
||||
|
||||
typedef struct ncinputlayer {
|
||||
@ -386,6 +387,8 @@ int terminfostr(char** gseq, const char* name);
|
||||
// initialized. set |utf8| if we've verified UTF8 output encoding.
|
||||
int interrogate_terminfo(tinfo* ti, const char* termname, unsigned utf8);
|
||||
|
||||
void free_terminfo_cache(tinfo* ti);
|
||||
|
||||
// perform queries that require writing to the terminal, and reading a
|
||||
// response, rather than simply reading the terminfo database. can result
|
||||
// in a lengthy delay or even block if the terminal doesn't respond.
|
||||
|
@ -777,14 +777,13 @@ init_banner(const notcurses* nc){
|
||||
term_fg_palindex(nc, stdout, nc->tcache.colors <= 256 ? 50 % nc->tcache.colors : 0x20e080);
|
||||
printf("\n notcurses %s by nick black et al", notcurses_version());
|
||||
term_fg_palindex(nc, stdout, nc->tcache.colors <= 256 ? 12 % nc->tcache.colors : 0x2080e0);
|
||||
printf("\n %d rows %d cols (%sB) %zuB cells %d colors%s%s\n"
|
||||
printf("\n %d rows %d cols (%sB) %zuB cells %d colors%s\n"
|
||||
" compiled with gcc-%s, %s-endian\n"
|
||||
" terminfo from %s\n",
|
||||
nc->stdplane->leny, nc->stdplane->lenx,
|
||||
bprefix(nc->stats.fbbytes, 1, prefixbuf, 0), sizeof(nccell),
|
||||
nc->tcache.colors,
|
||||
nc->tcache.RGBflag ? "+RGB" : "",
|
||||
nc->tcache.sixel ? "+Sixel" : "",
|
||||
__VERSION__,
|
||||
#ifdef __BYTE_ORDER__
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
@ -908,6 +907,16 @@ recursive_lock_init(pthread_mutex_t *lock){
|
||||
#endif
|
||||
}
|
||||
|
||||
int notcurses_check_pixel_support(notcurses* nc){
|
||||
if(query_term(&nc->tcache, nc->ttyfd)){
|
||||
return -1;
|
||||
}
|
||||
if(nc->tcache.pixelon){
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// FIXME cut this up into a few distinct pieces, yearrrgh
|
||||
notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
|
||||
notcurses_options defaultopts;
|
||||
@ -1033,12 +1042,6 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
|
||||
goto err;
|
||||
}
|
||||
if(ret->ttyfd >= 0){
|
||||
if(opts->flags & NCOPTION_VERIFY_SIXEL){
|
||||
if(query_term(&ret->tcache, ret->ttyfd)){
|
||||
free_plane(ret->stdplane);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if(ret->tcache.smkx && tty_emit(ret->tcache.smkx, ret->ttyfd)){
|
||||
free_plane(ret->stdplane);
|
||||
goto err;
|
||||
@ -1215,6 +1218,7 @@ int notcurses_stop(notcurses* nc){
|
||||
del_curterm(cur_term);
|
||||
ret |= pthread_mutex_destroy(&nc->statlock);
|
||||
ret |= pthread_mutex_destroy(&nc->pilelock);
|
||||
free_terminfo_cache(&nc->tcache);
|
||||
free(nc);
|
||||
}
|
||||
return ret;
|
||||
@ -2073,7 +2077,7 @@ bool notcurses_canchangecolor(const notcurses* nc){
|
||||
}
|
||||
|
||||
bool notcurses_canpixel(const notcurses* nc){
|
||||
return nc->tcache.sixel;
|
||||
return nc->tcache.pixelon;
|
||||
}
|
||||
|
||||
palette256* palette256_new(notcurses* nc){
|
||||
|
@ -71,6 +71,10 @@ apply_term_heuristics(tinfo* ti, const char* termname){
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_terminfo_cache(tinfo* ti){
|
||||
pthread_mutex_destroy(&ti->pixel_query);
|
||||
}
|
||||
|
||||
// termname is just the TERM environment variable. some details are not
|
||||
// exposed via terminfo, and we must make heuristic decisions based on
|
||||
// the detected terminal type, yuck :/.
|
||||
@ -184,6 +188,8 @@ int interrogate_terminfo(tinfo* ti, const char* termname, unsigned utf8){
|
||||
ti->fgop = "\x1b[39m";
|
||||
ti->bgop = "\x1b[49m";
|
||||
}
|
||||
pthread_mutex_init(&ti->pixel_query, NULL);
|
||||
ti->pixel_query_done = false;
|
||||
if(apply_term_heuristics(ti, termname)){
|
||||
return -1;
|
||||
}
|
||||
@ -229,8 +235,8 @@ query_sixel(tinfo* ti, int fd){
|
||||
state = DONE;
|
||||
}else if(in == '4'){
|
||||
if(!ti->pixelon){
|
||||
ti->pixelon = strdup("\ePq");
|
||||
ti->pixeloff = strdup("\e\\");
|
||||
ti->pixelon = "\ePq";
|
||||
ti->pixeloff = "\e\\";
|
||||
} // FIXME else warning?
|
||||
}
|
||||
break;
|
||||
@ -245,10 +251,18 @@ query_sixel(tinfo* ti, int fd){
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fd must be a real terminal, and must not be in nonblocking mode
|
||||
// fd must be a real terminal, and must not be in nonblocking mode. uses the
|
||||
// pthread_mutex_t of |ti| to only act once.
|
||||
int query_term(tinfo* ti, int fd){
|
||||
if(query_sixel(ti, fd)){
|
||||
if(fd < 0){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
int ret = 0;
|
||||
pthread_mutex_lock(&ti->pixel_query);
|
||||
if(!ti->pixel_query_done){
|
||||
ret = query_sixel(ti, fd);
|
||||
ti->pixel_query_done = true;
|
||||
}
|
||||
pthread_mutex_unlock(&ti->pixel_query);
|
||||
return ret;
|
||||
}
|
||||
|
@ -143,6 +143,9 @@ auto perframe(struct ncvisual* ncv, struct ncvisual_options* vopts,
|
||||
}else if(keyp >= '0' && keyp <= '8'){ // FIXME eliminate ctrl/alt
|
||||
marsh->blitter = static_cast<ncblitter_e>(keyp - '0');
|
||||
vopts->blitter = marsh->blitter;
|
||||
if(vopts->blitter == NCBLIT_PIXEL){
|
||||
notcurses_check_pixel_support(nc);
|
||||
}
|
||||
continue;
|
||||
}else if(keyp == NCKey::Up){
|
||||
// FIXME
|
||||
@ -280,6 +283,9 @@ int direct_mode_player(int argc, char** argv, ncscale_e scalemode, ncblitter_e b
|
||||
return -1;
|
||||
}
|
||||
bool failed = false;
|
||||
if(blitter == NCBLIT_PIXEL){
|
||||
dm.check_pixel_support();
|
||||
}
|
||||
{
|
||||
for(auto i = 0 ; i < argc ; ++i){
|
||||
try{
|
||||
@ -303,7 +309,6 @@ auto main(int argc, char** argv) -> int {
|
||||
float timescale, displaytime;
|
||||
ncscale_e scalemode;
|
||||
notcurses_options ncopts{};
|
||||
ncopts.flags = NCOPTION_VERIFY_SIXEL;
|
||||
ncblitter_e blitter = NCBLIT_PIXEL;
|
||||
bool quiet = false;
|
||||
bool loop = false;
|
||||
@ -348,6 +353,9 @@ auto main(int argc, char** argv) -> int {
|
||||
vopts.n = *stdn;
|
||||
vopts.scaling = scalemode;
|
||||
vopts.blitter = blitter;
|
||||
if(vopts.blitter == NCBLIT_PIXEL){
|
||||
notcurses_check_pixel_support(nc);
|
||||
}
|
||||
do{
|
||||
struct marshal marsh = {
|
||||
.subtitle_plane = nullptr,
|
||||
|
Loading…
x
Reference in New Issue
Block a user