view: support -m argument for margins #551

This commit is contained in:
nick black 2020-04-29 03:24:11 -04:00
parent 6e8a0bebae
commit 84107c547d
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC
9 changed files with 86 additions and 52 deletions

View File

@ -98,6 +98,11 @@ typedef struct notcurses_options {
int margin_t, margin_r, margin_b, margin_l; int margin_t, margin_r, margin_b, margin_l;
} notcurses_options; } 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 // 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 // be a tty. You'll usually want stdout. Returns NULL on error, including any
// failure initializing terminfo. // failure initializing terminfo.

View File

@ -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**. **-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. **-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. 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 # NOTES
Optimal display requires a terminal advertising the **rgb** terminfo(5) Optimal display requires a terminal advertising the **rgb** terminfo(5)

View File

@ -97,6 +97,7 @@ particular EGC is heavily reused within a plane.
A few high-level widgets are included, all built atop ncplanes: 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_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_multiselector(3)** for selecting one or more items from a set
* **notcurses_plot(3)** for drawing histograms and lineplots * **notcurses_plot(3)** for drawing histograms and lineplots

View File

@ -23,7 +23,9 @@ typedef struct notcurses_options {
} 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 # 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 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 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 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 ## Fatal signals

View File

@ -785,6 +785,11 @@ typedef struct notcurses_options {
int margin_t, margin_r, margin_b, margin_l; int margin_t, margin_r, margin_b, margin_l;
} notcurses_options; } 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 // 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 // be a tty. You'll usually want stdout. Returns NULL on error, including any
// failure initializing terminfo. // failure initializing terminfo.

View File

@ -79,6 +79,7 @@ typedef struct notcurses_options {
int margin_t, margin_r, margin_b, margin_l; int margin_t, margin_r, margin_b, margin_l;
} notcurses_options; } notcurses_options;
struct notcurses* notcurses_init(const notcurses_options*, FILE*); 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_stop(struct notcurses*);
int notcurses_render(struct notcurses*); int notcurses_render(struct notcurses*);
struct ncplane* notcurses_stdplane(struct notcurses*); struct ncplane* notcurses_stdplane(struct notcurses*);

View File

@ -127,53 +127,6 @@ usage(const char* exe, int status){
exit(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* static demoresult*
ext_demos(struct notcurses* nc, const char* spec, bool ignore_failures){ ext_demos(struct notcurses* nc, const char* spec, bool ignore_failures){
int ret = 0; int ret = 0;
@ -255,7 +208,11 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* ignore_failure
} }
break; break;
}case 'm':{ }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); usage(*argv, EXIT_FAILURE);
} }
break; break;

View File

@ -1897,3 +1897,45 @@ bool ncplane_set_scrolling(ncplane* n, bool scrollp){
n->scrolling = scrollp; n->scrolling = scrollp;
return old; 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;
}

View File

@ -17,10 +17,11 @@ static void usage(std::ostream& os, const char* name, int exitcode)
__attribute__ ((noreturn)); __attribute__ ((noreturn));
void usage(std::ostream& o, const char* name, int exitcode){ 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 << " -k: don't use the alternate screen\n";
o << " -l loglevel: integer between 0 and 9, goes to stderr'\n"; o << " -l loglevel: integer between 0 and 9, goes to stderr'\n";
o << " -s scaletype: one of 'none', 'scale', or 'stretch'\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; o << " -d mult: non-negative floating point scale for frame time" << std::endl;
exit(exitcode); exit(exitcode);
} }
@ -104,7 +105,7 @@ int handle_opts(int argc, char** argv, notcurses_options& opts, float* timescale
*timescale = 1.0; *timescale = 1.0;
*scalemode = NCScale::Scale; *scalemode = NCScale::Scale;
int c; 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){ switch(c){
case 'h': case 'h':
usage(std::cout, argv[0], EXIT_SUCCESS); 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':{ case 'k':{
opts.inhibit_alternate_screen = true; opts.inhibit_alternate_screen = true;
break; 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':{ }case 'd':{
std::stringstream ss; std::stringstream ss;
ss << optarg; ss << optarg;