diff --git a/include/notcurses.h b/include/notcurses.h index 77a57fa33..a53c814db 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -98,6 +98,23 @@ typedef struct cell { uint64_t channels; // + 8b == 16b } cell; +// These log levels consciously map cleanly to those of libav; notcurses itself +// 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. +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 doin' 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_e; + // Configuration for notcurses_init(). typedef struct notcurses_options { // The name of the terminfo database entry describing this terminal. If NULL, @@ -125,6 +142,9 @@ typedef struct notcurses_options { // If non-NULL, notcurses_render() will write each rendered frame to this // FILE* in addition to outfp. This is used primarily for debugging. FILE* renderfp; + // Progressively higher log levels result in more logging to stderr. By + // default, nothing is printed to stderr once fullscreen service begins. + ncloglevel_e loglevel; } notcurses_options; // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must diff --git a/src/demo/demo.c b/src/demo/demo.c index 89698f244..c7a54ebd7 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -66,9 +66,10 @@ struct timespec demodelay = { static void usage(const char* exe, int status){ FILE* out = status == EXIT_SUCCESS ? stdout : stderr; - fprintf(out, "usage: %s [ -hHVkc ] [ -d mult ] [ -f renderfile ] demospec\n", exe); + fprintf(out, "usage: %s [ -hHVkc ] [ -l loglevel ] [ -d mult ] [ -f renderfile ] demospec\n", exe); fprintf(out, " -h: this message\n"); fprintf(out, " -V: print program name and version\n"); + fprintf(out, " -l: logging level (%d: silent..%d: manic)\n", NCLOGLEVEL_SILENT, NCLOGLEVEL_TRACE); fprintf(out, " -H: deploy the HUD\n"); fprintf(out, " -k: keep screen; do not switch to alternate\n"); fprintf(out, " -d: delay multiplier (float)\n"); @@ -287,7 +288,7 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* use_hud){ int c; *use_hud = false; memset(opts, 0, sizeof(*opts)); - while((c = getopt(argc, argv, "HVhckd:f:p:")) != EOF){ + while((c = getopt(argc, argv, "HVhckl:d:f:p:")) != EOF){ switch(c){ case 'H': *use_hud = true; @@ -295,7 +296,19 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* use_hud){ case 'h': usage(*argv, EXIT_SUCCESS); break; - case 'V': + case 'l':{ + int loglevel; + if(sscanf(optarg, "%d", &loglevel) != 1){ + fprintf(stderr, "Couldn't get an int from %s\n", optarg); + usage(*argv, EXIT_FAILURE); + } + opts->loglevel = loglevel; + if(opts->loglevel < NCLOGLEVEL_SILENT || opts->loglevel > NCLOGLEVEL_TRACE){ + fprintf(stderr, "Invalid log level: %d\n", opts->loglevel); + usage(*argv, EXIT_FAILURE); + } + break; + }case 'V': printf("notcurses-demo version %s\n", notcurses_version()); exit(EXIT_SUCCESS); case 'c': diff --git a/src/lib/internal.h b/src/lib/internal.h index 0d8047f99..cb5be281f 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -193,7 +193,7 @@ int prep_special_keys(notcurses* nc); void input_free_esctrie(struct esctrie** trie); // initialize libav -int ncvisual_init(void); +int ncvisual_init(int loglevel); static inline void ncplane_lock(const ncplane* n){ diff --git a/src/lib/libav.c b/src/lib/libav.c index 60c643b04..84fdbdd11 100644 --- a/src/lib/libav.c +++ b/src/lib/libav.c @@ -437,8 +437,8 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, int* averr, return -1; } -int ncvisual_init(void){ - av_log_set_level(AV_LOG_QUIET); // FIXME make this configurable? +int ncvisual_init(int loglevel){ + av_log_set_level(loglevel); // FIXME could also use av_log_set_callback() and capture the message... return 0; } @@ -491,7 +491,8 @@ ncvisual* ncvisual_open_plane(notcurses* nc, const char* filename, return NULL; } -int ncvisual_init(void){ +int ncvisual_init(int loglevel){ + (void)loglevel; return 0; } #endif diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 609ca7cdb..6d5981d06 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -332,8 +332,8 @@ ncplane_resize_internal(ncplane* n, int keepy, int keepx, int keepleny, } int rows, cols; ncplane_dim_yx(n, &rows, &cols); -/*fprintf(stderr, "NCPLANE(RESIZING) to %dx%d at %d/%d (keeping %dx%d from %d/%d)\n", - ylen, xlen, yoff, xoff, keepleny, keeplenx, keepy, keepx);*/ +//fprintf(stderr, "NCPLANE(RESIZING) to %dx%d at %d/%d (keeping %dx%d from %d/%d)\n", +// ylen, xlen, yoff, xoff, keepleny, keeplenx, keepy, keepx); // we're good to resize. we'll need alloc up a new framebuffer, and copy in // those elements we're retaining, zeroing out the rest. alternatively, if // we've shrunk, we will be filling the new structure. @@ -650,6 +650,25 @@ void notcurses_reset_stats(notcurses* nc, ncstats* stats){ pthread_mutex_unlock(&nc->lock); } +// Convert a notcurses log level to its ffmpeg equivalent. +static int +ffmpeg_log_level(ncloglevel_e level){ + switch(level){ + case NCLOGLEVEL_SILENT: return AV_LOG_QUIET; + case NCLOGLEVEL_PANIC: return AV_LOG_PANIC; + case NCLOGLEVEL_FATAL: return AV_LOG_FATAL; + case NCLOGLEVEL_ERROR: return AV_LOG_ERROR; + case NCLOGLEVEL_WARNING: return AV_LOG_WARNING; + case NCLOGLEVEL_INFO: return AV_LOG_INFO; + case NCLOGLEVEL_VERBOSE: return AV_LOG_VERBOSE; + case NCLOGLEVEL_DEBUG: return AV_LOG_DEBUG; + case NCLOGLEVEL_TRACE: return AV_LOG_TRACE; + default: break; + } + fprintf(stderr, "Invalid log level: %d\n", level); + return AV_LOG_TRACE; +} + notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ const char* encoding = nl_langinfo(CODESET); if(encoding == NULL || strcmp(encoding, "UTF-8")){ @@ -720,7 +739,7 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(interrogate_terminfo(ret, opts)){ goto err; } - if(ncvisual_init()){ + if(ncvisual_init(ffmpeg_log_level(opts->loglevel))){ goto err; } if((ret->stdscr = create_initial_ncplane(ret)) == NULL){ diff --git a/src/view/view.cpp b/src/view/view.cpp index ee0daac1e..1930fc119 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -94,7 +94,10 @@ int main(int argc, char** argv){ std::cerr << "Error decoding " << argv[i] << ": " << errbuf.data() << std::endl; return EXIT_FAILURE; } - notcurses_getc_blocking(nc, nullptr); + char32_t ie = notcurses_getc_blocking(nc, nullptr); + if(ie == (char32_t)-1){ + break; + } ncvisual_destroy(ncv); } if(notcurses_stop(nc)){