diff --git a/doc/man/man1/ncplayer.1.md b/doc/man/man1/ncplayer.1.md index 07acb9559..2121bd27e 100644 --- a/doc/man/man1/ncplayer.1.md +++ b/doc/man/man1/ncplayer.1.md @@ -27,7 +27,7 @@ Only applies to multiframe media such as video and animated images. Not supporte the "press any key to continue" prompt will not be displayed. **seconds** may be any non-negative number. -**-l** ***loglevel***: Log between everything (loglevel 8) and nothing (loglevel 0) to stderr. +**-l** ***loglevel***: Log between everything (loglevel 7) and nothing (loglevel 0) to stderr. **-s** ***scalemode***: Scaling mode, one of **none**, **hires**, **scale**, **scalehi**, or **stretch**. diff --git a/doc/man/man1/notcurses-demo.1.md b/doc/man/man1/notcurses-demo.1.md index 7f12be36e..931d2b0ec 100644 --- a/doc/man/man1/notcurses-demo.1.md +++ b/doc/man/man1/notcurses-demo.1.md @@ -57,7 +57,7 @@ terminal, and will refuse to run in anything smaller than 80x24. **-d** ***delaymult***: Apply a non-negative rational multiplier to the standard delay of 1s. -**-l** ***loglevel***: Log between everything (loglevel 8) and nothing (loglevel 0) to stderr. +**-l** ***loglevel***: Log between everything (loglevel 7) and nothing (loglevel 0) to stderr. **-f** ***renderfile***: Render each frame to ***renderfile*** in addition to the screen. diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index d4d975172..b76ca386d 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -824,17 +824,18 @@ nccell_load_egc32(struct ncplane* n, nccell* c, uint32_t egc){ // does not use this full granularity. The log level does not affect the opening // and closing banners, which can be disabled via the notcurses_option struct's // 'suppress_banner'. Note that if stderr is connected to the same terminal on -// which we're rendering, any kind of logging will disrupt the output. +// which we're rendering, any kind of logging will disrupt the output (which is +// undesirable). The "default" zero value is NCLOGLEVEL_PANIC. typedef enum { - NCLOGLEVEL_SILENT, // default. print nothing once fullscreen service begins - NCLOGLEVEL_PANIC, // print diagnostics immediately related to crashing - NCLOGLEVEL_FATAL, // we're hanging around, but we've had a horrible fault - NCLOGLEVEL_ERROR, // we can't keep doing this, but we can do other things - NCLOGLEVEL_WARNING, // you probably don't want what's happening to happen - NCLOGLEVEL_INFO, // "standard information" - NCLOGLEVEL_VERBOSE, // "detailed information" - NCLOGLEVEL_DEBUG, // this is honestly a bit much - NCLOGLEVEL_TRACE, // there's probably a better way to do what you want + NCLOGLEVEL_SILENT = -1,// default. print nothing once fullscreen service begins + NCLOGLEVEL_PANIC = 0, // print diagnostics related to catastrophic failure + NCLOGLEVEL_FATAL = 1, // we're hanging around, but we've had a horrible fault + NCLOGLEVEL_ERROR = 2, // we can't keep doing this, but we can do other things + NCLOGLEVEL_WARNING = 3,// you probably don't want what's happening to happen + NCLOGLEVEL_INFO = 4, // "standard information" + NCLOGLEVEL_VERBOSE = 5,// "detailed information" + NCLOGLEVEL_DEBUG = 6, // this is honestly a bit much + NCLOGLEVEL_TRACE = 7, // there's probably a better way to do what you want } ncloglevel_e; // Bits for notcurses_options->flags. diff --git a/src/demo/fission.c b/src/demo/fission.c index 18d1f47a7..836f136f3 100644 --- a/src/demo/fission.c +++ b/src/demo/fission.c @@ -88,7 +88,7 @@ shuffle_in(struct ncplane** arr, int count, struct ncplane* n){ } arr = tmp; // location of new element - int pos = random() % (count + 1); + int pos = rand() % (count + 1); if(pos < count){ // move everything, starting at our new location, one spot right memmove(arr + pos + 1, arr + pos, sizeof(*arr) * (count - pos)); diff --git a/src/info/main.c b/src/info/main.c index 33f269451..0610b70f8 100644 --- a/src/info/main.c +++ b/src/info/main.c @@ -424,7 +424,7 @@ tinfo_debug_styles(const notcurses* nc, struct ncplane* n, const char* indent){ tinfo_debug_style(n, "struck", NCSTYLE_STRUCK, ' '); tinfo_debug_style(n, "ucurl", NCSTYLE_UNDERCURL, ' '); tinfo_debug_style(n, "uline", NCSTYLE_UNDERLINE, ' '); - tinfo_debug_cap(n, "u7", get_escape(ti, ESCAPE_DSRCPR), ' '); + tinfo_debug_cap(n, "u7", get_escape(ti, ESCAPE_U7), ' '); tinfo_debug_cap(n, "ccc", ti->caps.can_change_colors, ' '); tinfo_debug_cap(n, "rgb", ti->caps.rgb, ' '); finish_line(n); diff --git a/src/lib/direct.c b/src/lib/direct.c index 13bf1e2aa..74615155a 100644 --- a/src/lib/direct.c +++ b/src/lib/direct.c @@ -255,7 +255,7 @@ cursor_yx_get(int ttyfd, const char* u7, int* y, int* x){ int ncdirect_cursor_move_yx(ncdirect* n, int y, int x){ const char* hpa = get_escape(&n->tcache, ESCAPE_HPA); const char* vpa = get_escape(&n->tcache, ESCAPE_VPA); - const char* u7 = get_escape(&n->tcache, ESCAPE_DSRCPR); + const char* u7 = get_escape(&n->tcache, ESCAPE_U7); if(y == -1){ // keep row the same, horizontal move only if(hpa){ return term_emit(tiparm(hpa, x), n->ttyfp, false); @@ -410,7 +410,7 @@ int ncdirect_cursor_yx(ncdirect* n, int* y, int* x){ if(n->ctermfd < 0){ return -1; } - const char* u7 = get_escape(&n->tcache, ESCAPE_DSRCPR); + const char* u7 = get_escape(&n->tcache, ESCAPE_U7); if(u7 == NULL){ fprintf(stderr, "Terminal doesn't support cursor reporting\n"); return -1; @@ -1411,7 +1411,7 @@ const nccapabilities* ncdirect_capabilities(const ncdirect* n){ } bool ncdirect_canget_cursor(const ncdirect* n){ - if(get_escape(&n->tcache, ESCAPE_DSRCPR) == NULL){ + if(get_escape(&n->tcache, ESCAPE_U7) == NULL){ return false; } if(n->ctermfd < 0){ diff --git a/src/lib/fbuf.h b/src/lib/fbuf.h new file mode 100644 index 000000000..2580dd660 --- /dev/null +++ b/src/lib/fbuf.h @@ -0,0 +1,267 @@ +#ifndef NOTCURSES_FBUF +#define NOTCURSES_FBUF + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include "compat/compat.h" + +// a growable buffer into which one can perform formatted i/o, like the +// ten thousand that came before it, and the ten trillion which shall +// come after. uses mmap (with huge pages, if possible) on unix and +// virtualalloc on windows. it can grow arbitrarily large. it does +// *not* maintain a NUL terminator, and can hold binary data. +// on Windows, we're using VirtualAlloc(). on BSD, we're using realloc(). +// on Linux, we're using mmap()+mremap(). + +typedef struct fbuf { + uint64_t size; + uint64_t used; + char* buf; +} fbuf; + +// header-only so that we can test it from notcurses-tester + +static inline char* +fbuf_at(fbuf* f, uint64_t at){ + if(at > f->used){ + return NULL; + } + return f->buf + at; +} + +#ifdef MAP_POPULATE +#ifdef MAP_UNINITIALIZED +#define MAPFLAGS (MAP_POPULATE | MAP_UNINITIALIZED) +#else +#define MAPFLAGS MAP_POPULATE +#endif +#else +#ifdef MAP_UNINITIALIZED +#define MAPFLAGS MAP_UNINITIALIZED +#else +#define MAPFLAGS 0 +#endif +#endif + +// ensure there is sufficient room to add |n| bytes to |f|. if necessary, +// enlarge the buffer, which might move it (invalidating any references +// therein). the amount added is based on the current size (and |n|). we +// never grow larger than SIZE_MAX / 2. +static inline int +fbuf_grow(fbuf* f, size_t n){ + assert(NULL != f->buf); + assert(0 != f->size); + size_t size = f->size; + if(size - f->used >= n){ + return 0; // we have enough space + } + while(SIZE_MAX / 2 >= size){ + size *= 2; + if(size - f->used < n){ + continue; + } + void* tmp; +#ifdef __linux__ + tmp = mremap(f->buf, f->size, size, MREMAP_MAYMOVE); + if(tmp == MAP_FAILED){ + return -1; + } +#else + tmp = realloc(f->buf, f->size); + if(tmp == NULL){ + return -1; + } +#endif + f->buf = (char*)tmp; // cast for c++ callers + f->size = size; + return 0; + } + // n (or our current buffer) is too large + return -1; +} + +// prepare (a significant amount of) initial space for the fbuf. +// pass 1 for |small| if it ought be...small. +static inline int +fbuf_initgrow(fbuf* f, unsigned small){ + assert(NULL == f->buf); + assert(0 == f->used); + assert(0 == f->size); + // we start with 2MiB, the huge page size on all of x86+PAE, + // ARMv7+LPAE, ARMv8, and x86-64. + // FIXME use GetLargePageMinimum() and sysconf + size_t size = small ? (4096 > BUFSIZ ? 4096 : BUFSIZ) : 0x200000lu; +#if defined(__linux__) + static bool hugepages_failed = false; // FIXME atomic + if(!hugepages_failed && !small){ + // mmap(2): hugetlb results in automatic stretch out to cover hugepage + f->buf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_HUGETLB | + MAP_PRIVATE | MAP_ANONYMOUS | MAPFLAGS , -1, 0); + if(f->buf == MAP_FAILED){ + hugepages_failed = true; + f->buf = NULL; + } + } + if(f->buf == NULL){ // try again without MAP_HUGETLB + f->buf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAPFLAGS , -1, 0); + } + if(f->buf == MAP_FAILED){ + f->buf = NULL; + return -1; + } +#else + f->buf = (char*)malloc(size); + if(f->buf == NULL){ + return -1; + } +#endif + f->size = size; + return 0; +} +#undef MAPFLAGS + +// prepare f with a small initial buffer. +static inline int +fbuf_init_small(fbuf* f){ + f->used = 0; + f->size = 0; + f->buf = NULL; + return fbuf_initgrow(f, 1); +} + +// prepare f with a large initial buffer. +static inline int +fbuf_init(fbuf* f){ + f->used = 0; + f->size = 0; + f->buf = NULL; + return fbuf_initgrow(f, 0); +} + +// reset usage, but don't shrink the buffer or anything +static inline void +fbuf_reset(fbuf* f){ + f->used = 0; +} + +static inline int +fbuf_putc(fbuf* f, char c){ + if(fbuf_grow(f, 1)){ + return -1; + } + *fbuf_at(f, f->used++) = c; + return 1; +} + +static inline int +fbuf_putn(fbuf* f, const char* s, size_t len){ + if(fbuf_grow(f, len)){ + return -1; + } + memcpy(f->buf + f->used, s, len); + f->used += len; + return len; +} + +static inline int +fbuf_puts(fbuf* f, const char* s){ + size_t slen = strlen(s); + return fbuf_putn(f, s, slen); +} + +static inline int +fbuf_putint(fbuf* f, int n){ + if(fbuf_grow(f, 10)){ // 32-bit int might require up to 10 digits + return -1; + } + uint64_t r = snprintf(f->buf + f->used, f->size - f->used, "%d", n); + if(r > f->size - f->used){ + assert(r <= f->size - f->used); + return -1; // FIXME grow? + } + f->used += r; + return r; +} + +// FIXME eliminate this, ideally +__attribute__ ((format (printf, 2, 3))) +static inline int +fbuf_printf(fbuf* f, const char* fmt, ...){ + if(fbuf_grow(f, BUFSIZ) < 0){ + return -1; + } + va_list va; + va_start(va, fmt); + int r = vsnprintf(f->buf + f->used, f->size - f->used, fmt, va); + va_end(va); + if((size_t)r >= f->size - f->used){ + return -1; + } + assert(r >= 0); + f->used += r; + return r; +} + +// emit an escape; obviously you can't flush here +static inline int +fbuf_emit(fbuf* f, const char* esc){ + if(!esc){ + return -1; + } + if(fbuf_puts(f, esc) < 0){ + //logerror("error emitting escape (%s)\n", strerror(errno)); + return -1; + } + return 0; +} + +// releases the resources held by f. f itself is not freed. +static inline void +fbuf_free(fbuf* f){ + if(f){ +// logdebug("Releasing from %" PRIu32 "B (%" PRIu32 "B)\n", f->size, f->used); + if(f->buf){ +#if __linux__ + if(munmap(f->buf, f->size)){ + //logwarn("Error unmapping alloc (%s)\n", strerror(errno)); + } +#else + free(f->buf); +#endif + f->buf = NULL; + } + f->size = 0; + f->used = 0; + } +} + +// attempt to write the contents of |f| to the FILE |fp|, if there are any +// contents, and free the fbuf either way. if |flushfp| is set, fflush(fp). +static inline int +fbuf_finalize(fbuf* f, FILE* fp, bool flushfp){ + int ret = 0; + if(f->used){ + if(fwrite(f->buf, f->used, 1, fp) != 1){ + ret = -1; + } + } + if(flushfp && ret == 0 && fflush(fp) == EOF){ + ret = -1; + } + fbuf_free(f); + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/input.h b/src/lib/input.h index 14001ee68..af2d58f1d 100644 --- a/src/lib/input.h +++ b/src/lib/input.h @@ -33,6 +33,7 @@ typedef enum { TERMINAL_ITERM, // XTVERSION == 'iTerm2 [ver]' TERMINAL_TERMINOLOGY, // TDA: "~~TY" TERMINAL_APPLE, // Terminal.App, determined by TERM_PROGRAM + macOS + TERMINAL_MSTERMINAL, // Microsoft Windows Terminal } queried_terminals_e; // sets up the input layer, building a trie of escape sequences and their diff --git a/src/lib/termdesc.c b/src/lib/termdesc.c index 5d54c67e4..87a4a1152 100644 --- a/src/lib/termdesc.c +++ b/src/lib/termdesc.c @@ -261,41 +261,6 @@ compare_versions(const char* restrict v1, const char* restrict v2){ return 0; } -// tlen -- size of escape table. tused -- used bytes in same. -// returns -1 if the starting location is >= 65535. otherwise, -// copies tstr into the table, and sets up 1-biased index. -static int -grow_esc_table(tinfo* ti, const char* tstr, escape_e esc, - size_t* tlen, size_t* tused){ - // the actual table can grow past 64KB, but we can't start there, as - // we only have 16-bit indices. - if(*tused >= 65535){ - fprintf(stderr, "Can't add escape %d to full table\n", esc); - return -1; - } - if(get_escape(ti, esc)){ - fprintf(stderr, "Already defined escape %d (%s)\n", - esc, get_escape(ti, esc)); - return -1; - } - size_t slen = strlen(tstr) + 1; // count the nul term - if(*tlen - *tused < slen){ - // guaranteed to give us enough space to add tstr (and then some) - size_t newsize = *tlen + 4020 + slen; // don't pull two pages ideally - char* tmp = realloc(ti->esctable, newsize); - if(tmp == NULL){ - return -1; - } - ti->esctable = tmp; - *tlen = newsize; - } - // we now are guaranteed sufficient space to copy tstr - memcpy(ti->esctable + *tused, tstr, slen); - ti->escindices[esc] = *tused + 1; // one-bias - *tused += slen; - return 0; -} - // Tertiary Device Attributes, necessary to identify VTE. // https://vt100.net/docs/vt510-rm/DA3.html // Replies with DCS ! | ... ST @@ -408,11 +373,11 @@ init_terminfo_esc(tinfo* ti, const char* name, escape_e idx, // terminal supports it, hah. static int add_u7_escape(tinfo* ti, size_t* tablelen, size_t* tableused){ - const char* u7 = get_escape(ti, ESCAPE_DSRCPR); + const char* u7 = get_escape(ti, ESCAPE_U7); if(u7){ return 0; // already present } - if(grow_esc_table(ti, DSRCPR, ESCAPE_DSRCPR, tablelen, tableused)){ + if(grow_esc_table(ti, DSRCPR, ESCAPE_U7, tablelen, tableused)){ return -1; } return 0; @@ -777,7 +742,7 @@ int interrogate_terminfo(tinfo* ti, int fd, const char* termname, unsigned utf8, { ESCAPE_CLEAR, "clear", }, { ESCAPE_OC, "oc", }, { ESCAPE_RMKX, "rmkx", }, - { ESCAPE_DSRCPR, "u7", }, + { ESCAPE_U7, "u7", }, { ESCAPE_MAX, NULL, }, }; size_t tablelen = 0; @@ -942,7 +907,7 @@ locate_cursor_simple(int fd, const char* u7, int* cursor_y, int* cursor_x){ // is valid, we can just camp there. otherwise, we need dance with potential // user input looking at infd. int locate_cursor(tinfo* ti, int* cursor_y, int* cursor_x){ - const char* u7 = get_escape(ti, ESCAPE_DSRCPR); + const char* u7 = get_escape(ti, ESCAPE_U7); if(u7 == NULL){ logwarn("No support in terminfo\n"); return -1; diff --git a/src/lib/termdesc.h b/src/lib/termdesc.h index 213d92777..0aa3634c7 100644 --- a/src/lib/termdesc.h +++ b/src/lib/termdesc.h @@ -29,42 +29,42 @@ struct ncsharedstats; // indexes into the table of fixed-width (16-bit) indices typedef enum { - ESCAPE_CUP, // "cup" move cursor to absolute x, y position - ESCAPE_HPA, // "hpa" move cursor to absolute horizontal position - ESCAPE_VPA, // "vpa" move cursor to absolute vertical position - ESCAPE_SETAF, // "setaf" set foreground color - ESCAPE_SETAB, // "setab" set background color - ESCAPE_OP, // "op" set foreground and background color to defaults - ESCAPE_FGOP, // set foreground only to default - ESCAPE_BGOP, // set background only to default - ESCAPE_SGR0, // "sgr0" turn off all styles - ESCAPE_CIVIS, // "civis" make the cursor invisiable - ESCAPE_CNORM, // "cnorm" restore the cursor to normal - ESCAPE_OC, // "oc" restore original colors - ESCAPE_SITM, // "sitm" start italics - ESCAPE_RITM, // "ritm" end italics - ESCAPE_CUU, // "cuu" move n cells up - ESCAPE_CUB, // "cub" move n cells back (left) - ESCAPE_CUF, // "cuf" move n cells forward (right) - ESCAPE_BOLD, // "bold" enter bold mode - ESCAPE_NOBOLD, // disable bold (ANSI but not terminfo, SGR 22) - ESCAPE_CUD, // "cud" move n cells down - ESCAPE_SMKX, // "smkx" keypad_xmit (keypad transmit mode) - ESCAPE_RMKX, // "rmkx" keypad_local - ESCAPE_SMCUP, // "smcup" enter alternate screen - ESCAPE_RMCUP, // "rmcup" leave alternate screen - ESCAPE_SMXX, // "smxx" start struckout - ESCAPE_SMUL, // "smul" start underline - ESCAPE_RMUL, // "rmul" end underline - ESCAPE_SMULX, // "Smulx" deparameterized: start extended underline - ESCAPE_SMULNOX, // "Smulx" deparameterized: kill underline - ESCAPE_RMXX, // "rmxx" end struckout - ESCAPE_SC, // "sc" push the cursor onto the stack - ESCAPE_RC, // "rc" pop the cursor off the stack - ESCAPE_CLEAR, // "clear" clear screen and home cursor - ESCAPE_INITC, // "initc" set up palette entry - ESCAPE_GETM, // "getm" get mouse events - ESCAPE_DSRCPR, // "u7" cursor position report + ESCAPE_CUP, // "cup" move cursor to absolute x, y position + ESCAPE_HPA, // "hpa" move cursor to absolute horizontal position + ESCAPE_VPA, // "vpa" move cursor to absolute vertical position + ESCAPE_SETAF, // "setaf" set foreground color + ESCAPE_SETAB, // "setab" set background color + ESCAPE_OP, // "op" set foreground and background color to defaults + ESCAPE_FGOP, // set foreground only to default + ESCAPE_BGOP, // set background only to default + ESCAPE_SGR0, // "sgr0" turn off all styles + ESCAPE_CIVIS, // "civis" make the cursor invisiable + ESCAPE_CNORM, // "cnorm" restore the cursor to normal + ESCAPE_OC, // "oc" restore original colors + ESCAPE_SITM, // "sitm" start italics + ESCAPE_RITM, // "ritm" end italics + ESCAPE_CUU, // "cuu" move n cells up + ESCAPE_CUB, // "cub" move n cells back (left) + ESCAPE_CUF, // "cuf" move n cells forward (right) + ESCAPE_BOLD, // "bold" enter bold mode + ESCAPE_NOBOLD, // disable bold (ANSI but not terminfo, SGR 22) + ESCAPE_CUD, // "cud" move n cells down + ESCAPE_SMKX, // "smkx" keypad_xmit (keypad transmit mode) + ESCAPE_RMKX, // "rmkx" keypad_local + ESCAPE_SMCUP, // "smcup" enter alternate screen + ESCAPE_RMCUP, // "rmcup" leave alternate screen + ESCAPE_SMXX, // "smxx" start struckout + ESCAPE_SMUL, // "smul" start underline + ESCAPE_RMUL, // "rmul" end underline + ESCAPE_SMULX, // "Smulx" deparameterized: start extended underline + ESCAPE_SMULNOX, // "Smulx" deparameterized: kill underline + ESCAPE_RMXX, // "rmxx" end struckout + ESCAPE_SC, // "sc" push the cursor onto the stack + ESCAPE_RC, // "rc" pop the cursor off the stack + ESCAPE_CLEAR, // "clear" clear screen and home cursor + ESCAPE_INITC, // "initc" set up palette entry + ESCAPE_GETM, // "getm" get mouse events + ESCAPE_U7, // "u7" cursor position report // Application synchronized updates, not present in terminfo // (https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec) ESCAPE_BSUM, // Begin Synchronized Update Mode @@ -231,6 +231,41 @@ char* termdesc_longterm(const tinfo* ti); int locate_cursor(tinfo* ti, int* cursor_y, int* cursor_x); +// tlen -- size of escape table. tused -- used bytes in same. +// returns -1 if the starting location is >= 65535. otherwise, +// copies tstr into the table, and sets up 1-biased index. +static inline int +grow_esc_table(tinfo* ti, const char* tstr, escape_e esc, + size_t* tlen, size_t* tused){ + // the actual table can grow past 64KB, but we can't start there, as + // we only have 16-bit indices. + if(*tused >= 65535){ + fprintf(stderr, "Can't add escape %d to full table\n", esc); + return -1; + } + if(get_escape(ti, esc)){ + fprintf(stderr, "Already defined escape %d (%s)\n", + esc, get_escape(ti, esc)); + return -1; + } + size_t slen = strlen(tstr) + 1; // count the nul term + if(*tlen - *tused < slen){ + // guaranteed to give us enough space to add tstr (and then some) + size_t newsize = *tlen + 4020 + slen; // don't pull two pages ideally + char* tmp = (char*)realloc(ti->esctable, newsize); // cast for c++ + if(tmp == NULL){ + return -1; + } + ti->esctable = tmp; + *tlen = newsize; + } + // we now are guaranteed sufficient space to copy tstr + memcpy(ti->esctable + *tused, tstr, slen); + ti->escindices[esc] = *tused + 1; // one-bias + *tused += slen; + return 0; +} + #ifdef __cplusplus } #endif diff --git a/src/lib/windows.c b/src/lib/windows.c new file mode 100644 index 000000000..17538470c --- /dev/null +++ b/src/lib/windows.c @@ -0,0 +1,37 @@ +#include "termdesc.h" +#include "internal.h" +#include "windows.h" + +// ti has been memset to all zeroes. windows configuration is static. +int prepare_windows_terminal(tinfo* ti, size_t* tablelen, size_t* tableused){ + const struct wtermdesc { + escape_e esc; + const char* tinfo; + } wterms[] = { + { ESCAPE_CUP, "\x1b[%i%p1%d;%p2%dH", }, + { ESCAPE_RMKX, "\x1b[?1l", }, + { ESCAPE_SMKX, "\x1b[?1h", }, + { ESCAPE_VPA, "\x1b[%i%p1%dd", }, + { ESCAPE_HPA, "\x1b[%i%p1%dG", }, + { ESCAPE_SC, "\x1b[s", }, + { ESCAPE_RC, "\x1b[u", }, + { ESCAPE_CLEAR, "\x1b[2J", }, + { ESCAPE_SMCUP, "\x1b[?1049h", }, + { ESCAPE_RMCUP, "\x1b[?1049l", }, + { ESCAPE_SETAF, "\x1b[38;5;%i%p1%dm", }, + { ESCAPE_SETAB, "\x1b[48;5;%i%p1%dm", }, + { ESCAPE_OP, "\x1b[39;49m", }, + { ESCAPE_CIVIS, "\x1b[?25l", }, + { ESCAPE_CNORM, "\x1b[?25h", }, + { ESCAPE_U7, "\x1b[6n", }, + { ESCAPE_MAX, NULL, } + }, *w; + for(w = wterms ; w->tinfo; ++w){ + if(grow_esc_table(ti, w->tinfo, w->esc, tablelen, tableused)){ + return -1; + } + } + ti->caps.rgb = true; + ti->caps.colors = 256; + return 0; +} diff --git a/src/lib/windows.h b/src/lib/windows.h new file mode 100644 index 000000000..93ce3b9c2 --- /dev/null +++ b/src/lib/windows.h @@ -0,0 +1,18 @@ +#ifndef NOTCURSES_WINDOWS +#define NOTCURSES_WINDOWS + +#ifdef __cplusplus +extern "C" { +#endif + +struct tinfo; + +// ti has been memset to all zeroes. windows configuration is static. +int prepare_windows_terminal(struct tinfo* ti, size_t* tablelen, + size_t* tableused); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/player/play.cpp b/src/player/play.cpp index b8fceff93..012862215 100644 --- a/src/player/play.cpp +++ b/src/player/play.cpp @@ -27,7 +27,7 @@ void usage(std::ostream& o, const char* name, int exitcode){ o << " -k: use direct mode (cannot be used with -L or -d)\n"; o << " -L: loop frames\n"; o << " -t seconds: delay t seconds after each file\n"; - o << " -l loglevel: integer between 0 and 8, goes to stderr'\n"; + o << " -l loglevel: integer between 0 and 7, goes to stderr'\n"; o << " -s scaling: one of 'none', 'hires', 'scale', 'scalehi', or 'stretch'\n"; o << " -b blitter: one of 'ascii', 'half', 'quad', 'sex', 'braille', or 'pixel'\n"; o << " -m margins: margin, or 4 comma-separated margins\n"; @@ -259,11 +259,11 @@ auto handle_opts(int argc, char** argv, notcurses_options& opts, bool* quiet, int ll; ss >> ll; if(ll < NCLogLevel::Silent || ll > NCLogLevel::Trace){ - std::cerr << "Invalid log level [" << optarg << "] (wanted [0..8])\n"; + std::cerr << "Invalid log level [" << optarg << "] (wanted [-1..7])\n"; usage(std::cerr, argv[0], EXIT_FAILURE); } if(ll == 0 && strcmp(optarg, "0")){ - std::cerr << "Invalid log level [" << optarg << "] (wanted [0..8])\n"; + std::cerr << "Invalid log level [" << optarg << "] (wanted [-1..7])\n"; usage(std::cerr, argv[0], EXIT_FAILURE); } opts.loglevel = static_cast(ll); @@ -486,11 +486,6 @@ auto main(int argc, char** argv) -> int { std::cerr << "Couldn't set locale based off LANG\n"; return EXIT_FAILURE; } - sigset_t sigmask; - // ensure SIGWINCH is delivered only to a thread doing input - sigemptyset(&sigmask); - sigaddset(&sigmask, SIGWINCH); - pthread_sigmask(SIG_BLOCK, &sigmask, NULL); float timescale, displaytime; ncscale_e scalemode; notcurses_options ncopts{}; diff --git a/src/poc/zalgo.c b/src/poc/zalgo.c index 0b9823a88..e759d5bbd 100644 --- a/src/poc/zalgo.c +++ b/src/poc/zalgo.c @@ -6,6 +6,9 @@ int main(void){ .flags = NCOPTION_NO_ALTERNATE_SCREEN | NCOPTION_SUPPRESS_BANNERS, }; struct notcurses* nc = notcurses_core_init(&nops, NULL); + if(nc == NULL){ + return EXIT_FAILURE; + } struct ncplane* n = notcurses_stdplane(nc); const int y = 10; ncplane_putstr_aligned(n, y + 0, NCALIGN_CENTER, "Pack my box with five dozen liquor jugs."); diff --git a/src/tests/fbuf.cpp b/src/tests/fbuf.cpp new file mode 100644 index 000000000..f0b763e58 --- /dev/null +++ b/src/tests/fbuf.cpp @@ -0,0 +1,84 @@ +#include "main.h" +#include "fbuf.h" + +TEST_CASE("Fbuf") { + auto nc_ = testing_notcurses(); + if(!nc_){ + return; + } + int dimy, dimx; + struct ncplane* n_ = notcurses_stddim_yx(nc_, &dimy, &dimx); + REQUIRE(n_); + REQUIRE(0 == ncplane_cursor_move_yx(n_, 0, 0)); + + // check that upon successful initialization, we have some space + SUBCASE("FbufInit") { + fbuf f{}; + CHECK(0 == fbuf_init(&f)); + CHECK(0 < f.size); + CHECK(0 == f.used); + CHECK(nullptr != f.buf); + fbuf_free(&f); + } + + // fill the fbuf a character at a time + SUBCASE("FbufPutcCover") { + fbuf f{}; + CHECK(0 == fbuf_init(&f)); + CHECK(0 < f.size); + CHECK(0 == f.used); + CHECK(nullptr != f.buf); + auto oldsize = f.size; + for(size_t s = 0 ; s < oldsize ; ++s){ + CHECK(1 == fbuf_putc(&f, 'X')); + } + CHECK(f.used == oldsize); + CHECK(oldsize <= f.size); + fbuf_free(&f); + } + + // fill the fbuf with one large write + SUBCASE("FbufPutsCover") { + fbuf f{}; + CHECK(0 == fbuf_init(&f)); + CHECK(0 < f.size); + CHECK(0 == f.used); + CHECK(nullptr != f.buf); + auto oldsize = f.size; + auto erp = new char[oldsize + 1]; + memset(erp, 'X', oldsize); + erp[oldsize] = '\0'; + CHECK(oldsize == fbuf_puts(&f, erp)); + delete[] erp; + CHECK(f.used == oldsize); + CHECK(oldsize <= f.size); + fbuf_free(&f); + } + + // fill the fbuf with random writes + SUBCASE("FbufPutsCover") { + fbuf f{}; + CHECK(0 == fbuf_init(&f)); + CHECK(0 < f.size); + CHECK(0 == f.used); + CHECK(nullptr != f.buf); + auto oldsize = f.size; + auto erp = new char[oldsize + 1]; + size_t used = 0; + while(used < oldsize){ + size_t oldused = f.used; + size_t towrite = rand() % (oldsize - used) + 1; + memset(erp, rand() % 26 + 'A', towrite); + erp[towrite] = '\0'; + CHECK(towrite == fbuf_puts(&f, erp)); + CHECK(f.used == oldused + towrite); + used += towrite; + } + delete[] erp; + CHECK(f.used == oldsize); + CHECK(oldsize <= f.size); + fbuf_free(&f); + } + + CHECK(0 == notcurses_stop(nc_)); +}