diff --git a/USAGE.md b/USAGE.md index 76d208a27..2a982adba 100644 --- a/USAGE.md +++ b/USAGE.md @@ -98,6 +98,11 @@ typedef struct notcurses_options { int margin_t, margin_r, margin_b, margin_l; } notcurses_options; +// Lex a margin argument according to the standard notcurses definition. There +// can be either a single number, which will define all margins equally, or +// there can be four numbers separated by commas. +int notcurses_lex_margins(const char* op, notcurses_options* opts); + // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must // be a tty. You'll usually want stdout. Returns NULL on error, including any // failure initializing terminfo. diff --git a/doc/man/man1/notcurses-view.1.md b/doc/man/man1/notcurses-view.1.md index 3b0a6b7d6..b5970301d 100644 --- a/doc/man/man1/notcurses-view.1.md +++ b/doc/man/man1/notcurses-view.1.md @@ -23,10 +23,17 @@ and videos to the terminal. Media will be scaled to the terminal's size. **-s scalemode**: Scaling mode, one of **none**, **scale**, or **stretch**. +**-m margins**: Define rendering margins (see below). + **-k**: Inhibit use of the alternate screen. Necessary if you want the output left on your terminal after the program exits. files: Select which files to render, and what order to render them in. +Default margins are all 0, and thus the full screen will be rendered. Using +**-m**, margins can be supplied. Provide a single number to set all four margins +to the same value, or four comma-delimited values for the top, right, bottom, +and left margins respectively. Negative margins are illegal. + # NOTES Optimal display requires a terminal advertising the **rgb** terminfo(5) diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md index 57cc62c33..b82a392b1 100644 --- a/doc/man/man3/notcurses.3.md +++ b/doc/man/man3/notcurses.3.md @@ -97,6 +97,7 @@ particular EGC is heavily reused within a plane. A few high-level widgets are included, all built atop ncplanes: +* **notcurses_fds(3)** for dumping file descriptors/subprocesses to a plane * **notcurses_menu(3)** for menu bars at the top or bottom of the screen * **notcurses_multiselector(3)** for selecting one or more items from a set * **notcurses_plot(3)** for drawing histograms and lineplots diff --git a/doc/man/man3/notcurses_init.3.md b/doc/man/man3/notcurses_init.3.md index 3d579fa30..8920107e1 100644 --- a/doc/man/man3/notcurses_init.3.md +++ b/doc/man/man3/notcurses_init.3.md @@ -23,7 +23,9 @@ typedef struct notcurses_options { } notcurses_options; ``` -**struct notcurses* notcurses_init(const struct notcurses_options* opts, FILE* fp);** +**int notcurses_lex_margins(const char* op, notcurses_options* opts);** + +**struct notcurses* notcurses_init(const notcurses_options* opts, FILE* fp);** # DESCRIPTION @@ -67,7 +69,11 @@ the full screen cleared, followed by rendering to a subregion. Inhibiting the alternate screen plus margins will see rendering to a subregion, with the screen outside this region not cleared. This is the only means by which existing output can be undisturbed by notcurses. Margins are best-effort. Supplying any -negative margin is an error. +negative margin is an error. **notcurses_lex_margins** provides lexing a +margin argument expression in one of two forms: + +* a single number, which will be applied to all sides, or +* four comma-delimited numbers, applied to top, right, bottom, and left. ## Fatal signals diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index 1d5aba8d0..0e197821a 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -785,6 +785,11 @@ typedef struct notcurses_options { int margin_t, margin_r, margin_b, margin_l; } notcurses_options; +// Lex a margin argument according to the standard notcurses definition. There +// can be either a single number, which will define all margins equally, or +// there can be four numbers separated by commas. +API int notcurses_lex_margins(const char* op, notcurses_options* opts); + // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must // be a tty. You'll usually want stdout. Returns NULL on error, including any // failure initializing terminfo. diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index af4d105c8..e205c764e 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -79,6 +79,7 @@ typedef struct notcurses_options { int margin_t, margin_r, margin_b, margin_l; } notcurses_options; struct notcurses* notcurses_init(const notcurses_options*, FILE*); +int notcurses_lex_margins(const char* op, notcurses_options* opts); int notcurses_stop(struct notcurses*); int notcurses_render(struct notcurses*); struct ncplane* notcurses_stdplane(struct notcurses*); diff --git a/src/demo/demo.c b/src/demo/demo.c index 7aa605269..d08f414e8 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -127,53 +127,6 @@ usage(const char* exe, int status){ exit(status); } -// extract an integer, which must be non-negative, and followed by either a -// comma or a NUL terminator. -static int -lex_long(const char* op, int* i, char** endptr){ - errno = 0; - long l = strtol(op, endptr, 10); - if(l < 0 || (l == LONG_MAX && errno == ERANGE) || (l > INT_MAX)){ - fprintf(stderr, "Invalid margin: %s\n", op); - return -1; - } - if((**endptr != ',' && **endptr) || *endptr == op){ - fprintf(stderr, "Invalid margin: %s\n", op); - return -1; - } - *i = l; - return 0; -} - -static int -lex_margins(const char* op, notcurses_options* opts){ - if(opts->margin_t || opts->margin_r || opts->margin_b || opts->margin_l){ - fprintf(stderr, "Provided margins twice!\n"); - return -1; - } - char* eptr; - if(lex_long(op, &opts->margin_t, &eptr)){ - return -1; - } - if(!*eptr){ // allow a single value to be specified for all four margins - opts->margin_r = opts->margin_l = opts->margin_b = opts->margin_t; - return 0; - } - op = ++eptr; // once here, we require four values - if(lex_long(op, &opts->margin_r, &eptr) || !*eptr){ - return -1; - } - op = ++eptr; - if(lex_long(op, &opts->margin_b, &eptr) || !*eptr){ - return -1; - } - op = ++eptr; - if(lex_long(op, &opts->margin_l, &eptr) || *eptr){ // must end in NUL - return -1; - } - return 0; -} - static demoresult* ext_demos(struct notcurses* nc, const char* spec, bool ignore_failures){ int ret = 0; @@ -255,7 +208,11 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* ignore_failure } break; }case 'm':{ - if(lex_margins(optarg, opts)){ + if(opts->margin_t || opts->margin_r || opts->margin_b || opts->margin_l){ + fprintf(stderr, "Provided margins twice!\n"); + usage(*argv, EXIT_FAILURE); + } + if(notcurses_lex_margins(optarg, opts)){ usage(*argv, EXIT_FAILURE); } break; diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index b51070e37..56322455a 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -1897,3 +1897,45 @@ bool ncplane_set_scrolling(ncplane* n, bool scrollp){ n->scrolling = scrollp; return old; } + +// extract an integer, which must be non-negative, and followed by either a +// comma or a NUL terminator. +static int +lex_long(const char* op, int* i, char** endptr){ + errno = 0; + long l = strtol(op, endptr, 10); + if(l < 0 || (l == LONG_MAX && errno == ERANGE) || (l > INT_MAX)){ + fprintf(stderr, "Invalid margin: %s\n", op); + return -1; + } + if((**endptr != ',' && **endptr) || *endptr == op){ + fprintf(stderr, "Invalid margin: %s\n", op); + return -1; + } + *i = l; + return 0; +} + +int notcurses_lex_margins(const char* op, notcurses_options* opts){ + char* eptr; + if(lex_long(op, &opts->margin_t, &eptr)){ + return -1; + } + if(!*eptr){ // allow a single value to be specified for all four margins + opts->margin_r = opts->margin_l = opts->margin_b = opts->margin_t; + return 0; + } + op = ++eptr; // once here, we require four values + if(lex_long(op, &opts->margin_r, &eptr) || !*eptr){ + return -1; + } + op = ++eptr; + if(lex_long(op, &opts->margin_b, &eptr) || !*eptr){ + return -1; + } + op = ++eptr; + if(lex_long(op, &opts->margin_l, &eptr) || *eptr){ // must end in NUL + return -1; + } + return 0; +} diff --git a/src/view/view.cpp b/src/view/view.cpp index 1e62f184b..8ce3e0275 100644 --- a/src/view/view.cpp +++ b/src/view/view.cpp @@ -17,10 +17,11 @@ static void usage(std::ostream& os, const char* name, int exitcode) __attribute__ ((noreturn)); void usage(std::ostream& o, const char* name, int exitcode){ - o << "usage: " << name << " [ -h ] [ -l loglevel ] [ -d mult ] [ -s scaletype ] [ -k ] files" << '\n'; + o << "usage: " << name << " [ -h ] [ -m margins ] [ -l loglevel ] [ -d mult ] [ -s scaletype ] [ -k ] files" << '\n'; o << " -k: don't use the alternate screen\n"; o << " -l loglevel: integer between 0 and 9, goes to stderr'\n"; o << " -s scaletype: one of 'none', 'scale', or 'stretch'\n"; + o << " -m margins: margin, or 4 comma-separated margins\n"; o << " -d mult: non-negative floating point scale for frame time" << std::endl; exit(exitcode); } @@ -104,7 +105,7 @@ int handle_opts(int argc, char** argv, notcurses_options& opts, float* timescale *timescale = 1.0; *scalemode = NCScale::Scale; int c; - while((c = getopt(argc, argv, "hl:d:s:k")) != -1){ + while((c = getopt(argc, argv, "hl:d:s:m:k")) != -1){ switch(c){ case 'h': usage(std::cout, argv[0], EXIT_SUCCESS); @@ -121,6 +122,15 @@ int handle_opts(int argc, char** argv, notcurses_options& opts, float* timescale case 'k':{ opts.inhibit_alternate_screen = true; break; + }case 'm':{ + if(opts.margin_t || opts.margin_r || opts.margin_b || opts.margin_l){ + std::cerr << "Provided margins twice!" << std::endl; + usage(std::cerr, argv[0], EXIT_FAILURE); + } + if(notcurses_lex_margins(optarg, &opts)){ + usage(std::cerr, argv[0], EXIT_FAILURE); + } + break; }case 'd':{ std::stringstream ss; ss << optarg;