diff --git a/README.md b/README.md index 84d860b09..054633f41 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Packages for Debian Unstable and Ubuntu Focal are available from [DSSCAW](https: * [Features missing relative to NCURSES](#features-missing-relative-to-ncurses) * [Adapting NCURSES programs](#adapting-ncurses-programs) * [Environment notes](#environment-notes) - * [DirectColor detection](#DirectColor-detection) + * [TrueColor detection](#TrueColor-detection) * [Fonts](#fonts) * [FAQs](#faqs) * [Supplemental material](#supplemental-material) @@ -57,9 +57,9 @@ Packages for Debian Unstable and Ubuntu Focal are available from [DSSCAW](https: notcurses abandons the X/Open Curses API bundled as part of the Single UNIX Specification. The latter shows its age, and seems not capable of making use of -terminal functionality such as unindexed 24-bit color ("DirectColor", not to be -confused with 8-bit indexed 24-bit color, aka "TrueColor" or (by NCURSES) as -"extended color"). For some necessary background, consult Thomas E. Dickey's +terminal functionality such as unindexed 24-bit color ("TrueColor", not to be +confused with the 8-bit indexed 24-bit "extended color" of NCURSES). +For some necessary background, consult Thomas E. Dickey's superb and authoritative [NCURSES FAQ](https://invisible-island.net/ncurses/ncurses.faq.html#xterm_16MegaColors). As such, notcurses is not a drop-in Curses replacement. It is almost certainly less portable, and definitely tested on less hardware. Sorry about that. @@ -212,6 +212,11 @@ typedef struct notcurses_options { // Progressively higher log levels result in more logging to stderr. By // default, nothing is printed to stderr once fullscreen service begins. ncloglevel_e loglevel; + // Desirable margins. If all are 0 (default), we will render to the entirety + // of the screen. If the screen is too small, we do what we can--this is + // strictly best-effort. Absolute coordinates are relative to the rendering + // area ((0, 0) is always the origin of the rendering area). + int margin_t, margin_r, margin_b, margin_l; } notcurses_options; // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must @@ -2498,12 +2503,12 @@ These are pretty obvious, implementation-wise. * The unit tests assume dimensions of at least 80x24. They might work in a smaller terminal. They might not. Don't file bugs on it. -### DirectColor detection +### TrueColor detection -notcurses aims to use only information found in the terminal's terminfo entry to detect capabilities, DirectColor +notcurses aims to use only information found in the terminal's terminfo entry to detect capabilities, TrueColor being one of them. Support for this is indicated by terminfo having a flag, added in NCURSES 6.1, named `RGB` set to `true`. However, as of today there are few and far between terminfo entries which have the capability in their -database entry and so DirectColor won't be used in most cases. Terminal emulators have had for years a kludge to +database entry and so TrueColor won't be used in most cases. Terminal emulators have had for years a kludge to work around this limitation of terminfo in the form of the `COLORTERM` environment variable which, if set to either `truecolor` or `24bit` does the job of indicating the capability of sending the escapes 48 and 38 together with a tripartite RGB (0 ≤ c ≤ 255 for all three components) to specify fore- and background colors. @@ -2590,7 +2595,7 @@ up someday **FIXME**. I study the history of NCURSES, primarily using Thomas E. Dickey's FAQ and the mailing list archives. * 2019-11-14: I file [Outcurses issue #56](https://github.com/dankamongmen/ncreels/issues/56) - regarding use of DirectColor in outcurses. This is partially inspired by + regarding use of TrueColor in outcurses. This is partially inspired by Lexi Summer Hale's essay [everything you ever wanted to know about terminals](http://xn--rpa.cc/irl/term.html). I get into contact with Thomas E. Dickey and confirm that what I'm hoping to do doesn't really fit in with the codified Curses API. diff --git a/doc/man/man1/notcurses-demo.1.md b/doc/man/man1/notcurses-demo.1.md index 8a9729fbb..5688ef9c7 100644 --- a/doc/man/man1/notcurses-demo.1.md +++ b/doc/man/man1/notcurses-demo.1.md @@ -9,13 +9,14 @@ notcurses-demo - Show off some notcurses features # SYNOPSIS **notcurses-demo** [**-h|--help**] [**-p path**] [**-d delaymult**] - [**-l loglevel**] [**-f renderfile**] [**-J jsonfile**] [**-ikVc**] demospec + [**-l loglevel**] [**-f renderfile**] [**-J jsonfile**] [**-m margins**] + [**-ikVc**] demospec # DESCRIPTION **notcurses-demo** demonstrates the capabilities of the notcurses library. It can be run in any terminal emulator or console with a correct terminfo(5) -database, but is at is best in a "DirectColor" 24bpp RGB environment. If +database, but is at is best in a 24bpp TrueColor RGB environment. If **notcurses-demo** seems to generate garbage, something is likely configured in a way that is going to prevent notcurses from working. @@ -53,6 +54,8 @@ At any time, press 'q' to quit. The demo is best run in at least an 80x45 termin **-J jsonfile**: Emit JSON summary of run to **jsonfile**. +**-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. **-c**: Do not attempt to seed the PRNG. This is useful when benchmarking. @@ -65,6 +68,11 @@ At any time, press 'q' to quit. The demo is best run in at least an 80x45 termin demospec: Select which demos to run, and what order to run them in. The default is **ixethbcgrwuvlfsjo**. See above for a list of demos. +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 Proper display requires: diff --git a/doc/man/man3/notcurses.3.md b/doc/man/man3/notcurses.3.md index 458e8baa6..7b3937326 100644 --- a/doc/man/man3/notcurses.3.md +++ b/doc/man/man3/notcurses.3.md @@ -17,7 +17,7 @@ notcurses - TUI library for modern terminal emulators notcurses builds atop the **terminfo(5)** abstraction layer to provide reasonably portable vivid character displays. It is an intellectual descendant of **ncurses(3NCURSES)**, but goes beyond that library (and the X/Open Curses -API it implements). notcurses is capable of subregion fades, 24bpp DirectColor, +API it implements). notcurses is capable of subregion fades, 24bpp TrueColor, transparency, multimedia, and safe multithreaded use. A program wishing to use notcurses will need to link it, ideally using the diff --git a/doc/man/man3/notcurses_init.3.md b/doc/man/man3/notcurses_init.3.md index 5bc37c1b7..25fb92d01 100644 --- a/doc/man/man3/notcurses_init.3.md +++ b/doc/man/man3/notcurses_init.3.md @@ -19,6 +19,7 @@ typedef struct notcurses_options { bool no_quit_sighandlers; bool no_winch_sighandler; FILE* renderfp; + int margin_t, margin_r, margin_b, margin_l; } notcurses_options; ``` @@ -58,6 +59,16 @@ information and some details of the configured terminal. This can be inhibited with **suppress_banner**. This will also inhibit the performance summary normally printed by **notcurses_stop(3)**. +Notcurses can render to a subregion of the terminal by specifying desired +margins on all four sides. By default, all margins are zero, and thus rendering +will be performed on the entirety of the viewing area. This is orthogonal to +use of the alternate screen; using the alternate screen plus margins will see +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. + ## Fatal signals It is important to reset the terminal before exiting, whether terminating due diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h index ef1c4fd66..e916b5371 100644 --- a/include/notcurses/notcurses.h +++ b/include/notcurses/notcurses.h @@ -107,8 +107,6 @@ mbswidth(const char* mbs){ return cols; } -#define NCPALETTESIZE 256 - // A cell corresponds to a single character cell on some plane, which can be // occupied by a single grapheme cluster (some root spacing glyph, along with // possible combining characters, which might span multiple columns). At any @@ -232,6 +230,11 @@ typedef struct notcurses_options { // Progressively higher log levels result in more logging to stderr. By // default, nothing is printed to stderr once fullscreen service begins. ncloglevel_e loglevel; + // Desirable margins. If all are 0 (default), we will render to the entirety + // of the screen. If the screen is too small, we do what we can--this is + // strictly best-effort. Absolute coordinates are relative to the rendering + // area ((0, 0) is always the origin of the rendering area). + int margin_t, margin_r, margin_b, margin_l; } notcurses_options; // Initialize a notcurses context on the connected terminal at 'fp'. 'fp' must @@ -953,6 +956,7 @@ API int ncplane_stain(struct ncplane* n, int ystop, int xstop, uint64_t ul, // excluding the base cell. API void ncplane_erase(struct ncplane* n); +#define NCPALETTESIZE 256 #define CELL_WIDEASIAN_MASK 0x8000000080000000ull #define CELL_BGDEFAULT_MASK 0x0000000040000000ull #define CELL_FGDEFAULT_MASK (CELL_BGDEFAULT_MASK << 32u) diff --git a/python/src/notcurses/build_notcurses.py b/python/src/notcurses/build_notcurses.py index 58365871e..3222953e8 100644 --- a/python/src/notcurses/build_notcurses.py +++ b/python/src/notcurses/build_notcurses.py @@ -72,6 +72,11 @@ typedef struct notcurses_options { // Progressively higher log levels result in more logging to stderr. By // default, nothing is printed to stderr once fullscreen service begins. ncloglevel_e loglevel; + // Desirable margins. If all are 0 (default), we will render to the entirety + // of the screen. If the screen is too small, we do what we can--this is + // strictly best-effort. Absolute coordinates are relative to the rendering + // area ((0, 0) is always the origin of the rendering area). + int margin_t, margin_r, margin_b, margin_l; } notcurses_options; struct notcurses* notcurses_init(const notcurses_options*, FILE*); int notcurses_stop(struct notcurses*); @@ -322,7 +327,7 @@ typedef struct multiselector_options { uint64_t bgchannels; // background channels, used only in body } multiselector_options; struct ncmultiselector* ncmultiselector_create(struct ncplane* n, int y, int x, const multiselector_options* opts); -int ncmultiselector_selected(struct ncmultiselector* n, bool* selected, unsigned n); +int ncmultiselector_selected(struct ncmultiselector* n, bool* selected, unsigned count); struct ncplane* ncmultiselector_plane(struct ncmultiselector* n); bool ncmultiselector_offer_input(struct ncmultiselector* n, const struct ncinput* nc); void ncmultiselector_destroy(struct ncmultiselector* n, char** item); diff --git a/rust/libnotcurses-sys/build.rs b/rust/libnotcurses-sys/build.rs index adfeb814f..613af3e67 100644 --- a/rust/libnotcurses-sys/build.rs +++ b/rust/libnotcurses-sys/build.rs @@ -21,7 +21,7 @@ fn main() { // to bindgen, and lets you build up options for // the resulting bindings. let bindings = bindgen::Builder::default() - .clang_arg("-I../../include") // FIXME pass via envvar? + .clang_arg("-I../../include -D_XOPEN_SOURCE") // FIXME pass via envvar? // The input header we would like to generate // bindings for. .header("wrapper.h") diff --git a/rust/notcurses/Cargo.toml b/rust/notcurses/Cargo.toml index 9fb64b8fb..ef54bb9b9 100644 --- a/rust/notcurses/Cargo.toml +++ b/rust/notcurses/Cargo.toml @@ -11,3 +11,4 @@ homepage = "https://nick-black.com/dankwiki/index.php/Notcurses" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +libnotcurses-sys = "^1.2.3" diff --git a/src/demo/demo.c b/src/demo/demo.c index c5c8607b6..f952d77f0 100644 --- a/src/demo/demo.c +++ b/src/demo/demo.c @@ -10,9 +10,9 @@ #include #include "demo.h" -// ansi terminal definition-4-life +// (non-)ansi terminal definition-4-life static const int MIN_SUPPORTED_ROWS = 24; -static const int MIN_SUPPORTED_COLS = 80; +static const int MIN_SUPPORTED_COLS = 76; // allow a bit of margin, sigh static int democount; static demoresult* results; @@ -120,7 +120,7 @@ static struct { static void usage(const char* exe, int status){ FILE* out = status == EXIT_SUCCESS ? stdout : stderr; - fprintf(out, "usage: %s [ -hVikc ] [ -p path ] [ -l loglevel ] [ -d mult ] [ -J jsonfile ] [ -f renderfile ] demospec\n", exe); + fprintf(out, "usage: %s [ -hVikc ] [ -m margins ] [ -p path ] [ -l loglevel ] [ -d mult ] [ -J jsonfile ] [ -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); @@ -131,6 +131,7 @@ usage(const char* exe, int status){ fprintf(out, " -J: emit JSON summary to file\n"); fprintf(out, " -c: constant PRNG seed, useful for benchmarking\n"); fprintf(out, " -p: data file path (default: %s)\n", NOTCURSES_SHARE); + fprintf(out, " -m: margin, or 4 comma-separated margins\n"); fprintf(out, "if no specification is provided, run %s\n", DEFAULT_DEMO); for(size_t i = 0 ; i < sizeof(demos) / sizeof(*demos) ; ++i){ if(demos[i].name){ @@ -140,6 +141,53 @@ 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; @@ -189,7 +237,7 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* ignore_failure *json_output = NULL; int c; memset(opts, 0, sizeof(*opts)); - while((c = getopt(argc, argv, "VhickJ:l:r:d:f:p:")) != EOF){ + while((c = getopt(argc, argv, "VhickJ:l:r:d:f:p:m:")) != EOF){ switch(c){ case 'h': usage(*argv, EXIT_SUCCESS); @@ -206,6 +254,11 @@ handle_opts(int argc, char** argv, notcurses_options* opts, bool* ignore_failure usage(*argv, EXIT_FAILURE); } break; + }case 'm':{ + if(lex_margins(optarg, opts)){ + usage(*argv, EXIT_FAILURE); + } + break; }case 'V': printf("notcurses-demo version %s\n", notcurses_version()); exit(EXIT_SUCCESS); diff --git a/src/demo/eagle.c b/src/demo/eagle.c index b4b811d95..bcb0bd89d 100644 --- a/src/demo/eagle.c +++ b/src/demo/eagle.c @@ -181,7 +181,8 @@ eagles(struct notcurses* nc){ }else if(e[i].yoff + height >= truey){ e[i].yoff = truey - height - 1; } - e[i].xoff += (random() % (truex / 80)) + 1; + int scale = truex >= 80 ? truex / 80 : 1; + e[i].xoff += (random() % scale) + 1; ncplane_move_yx(e[i].n, e[i].yoff, e[i].xoff); ++eaglesmoved; } diff --git a/src/demo/whiteout.c b/src/demo/whiteout.c index 106e6d147..d5ec2254d 100644 --- a/src/demo/whiteout.c +++ b/src/demo/whiteout.c @@ -211,7 +211,7 @@ message(struct ncplane* n, int maxy, int maxx, int num, int total, ncplane_printf_yx(n, 1, 4, " %03dx%03d (%d/%d) ", maxx, maxy, num + 1, total); ncplane_styles_off(n, NCSTYLE_ITALIC); ncplane_set_fg_rgb(n, 224, 128, 224); - ncplane_putstr_yx(n, 3, 1, " 🔥 unicode 13, resize awareness, 24b directcolor…🔥 "); + ncplane_putstr_yx(n, 3, 1, " 🔥 unicode 13, resize awareness, 24b truecolor…🔥 "); ncplane_set_fg_rgb(n, 255, 255, 255); return 0; } diff --git a/src/lib/internal.h b/src/lib/internal.h index 6522a1efd..f935febfa 100644 --- a/src/lib/internal.h +++ b/src/lib/internal.h @@ -227,7 +227,7 @@ typedef struct ncdirect { char* initc; // set a palette entry's RGB value char* oc; // restore original colors char* clear; // clear the screen - bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor + bool RGBflag; // terminfo-reported "RGB" flag for 24bpc truecolor bool CCCflag; // terminfo-reported "CCC" flag for palette set capability FILE* ttyfp; // FILE* for controlling tty, from opts->ttyfp palette256 palette; // 256-indexed palette can be used instead of/with RGB @@ -284,7 +284,7 @@ typedef struct notcurses { char* getm; // get mouse events char* initc; // set a palette entry's RGB value char* oc; // restore original colors - bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor + bool RGBflag; // terminfo-reported "RGB" flag for 24bpc truecolor bool CCCflag; // terminfo-reported "CCC" flag for palette set capability bool AMflag; // ti-reported "AM" flag for automatic movement to next line @@ -307,6 +307,9 @@ typedef struct notcurses { // be reset (semantics are relied upon by widgets for mouse click detection). uint64_t input_events; + // desired margins (best-effort only), copied in from notcurses_options + int margin_t, margin_b, margin_r, margin_l; + palette256 palette; // 256-indexed palette can be used instead of/with RGB bool palette_damage[NCPALETTESIZE]; struct esctrie* inputescapes; // trie of input escapes -> ncspecial_keys diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index 6e9c450fc..749cd1d02 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -283,8 +283,8 @@ ncplane_create(notcurses* nc, int rows, int cols, int yoff, int xoff){ p->leny = rows; p->lenx = cols; p->x = p->y = 0; - p->absx = xoff; - p->absy = yoff; + p->absx = xoff + nc->margin_l; + p->absy = yoff + nc->margin_t; p->attrword = 0; p->channels = 0; egcpool_init(&p->pool); @@ -301,7 +301,8 @@ ncplane_create(notcurses* nc, int rows, int cols, int yoff, int xoff){ // the z-buffer. clear out all cells. this is for a wholly new context. static ncplane* create_initial_ncplane(notcurses* nc, int dimy, int dimx){ - nc->stdscr = ncplane_create(nc, dimy, dimx, 0, 0); + nc->stdscr = ncplane_create(nc, dimy - (nc->margin_t + nc->margin_b), + dimx - (nc->margin_l + nc->margin_r), 0, 0); return nc->stdscr; } @@ -497,6 +498,15 @@ int notcurses_resize(notcurses* n, int* rows, int* cols){ if(update_term_dimensions(n->ttyfd, rows, cols)){ return -1; } + // FIXME can we emerge from the previous call with rows/cols <= 0? + *rows -= n->margin_t + n->margin_b; + if(*rows <= 0){ + *rows = 1; + } + *cols -= n->margin_l + n->margin_r; + if(*cols <= 0){ + *cols = 1; + } if(*rows == oldrows && *cols == oldcols){ return 0; // no change } @@ -551,7 +561,7 @@ query_rgb(void){ if(!rgb){ // RGB terminfo capability being a new thing (as of ncurses 6.1), it's not commonly found in // terminal entries today. COLORTERM, however, is a de-facto (if imperfect/kludgy) standard way - // of indicating DirectColor support for a terminal. The variable takes one of two case-sensitive + // of indicating TrueColor support for a terminal. The variable takes one of two case-sensitive // values: // // truecolor @@ -567,8 +577,7 @@ query_rgb(void){ } static int -interrogate_terminfo(notcurses* nc, const notcurses_options* opts, - int* dimy, int* dimx){ +interrogate_terminfo(notcurses* nc, const notcurses_options* opts, int* dimy, int* dimx){ update_term_dimensions(nc->ttyfd, dimy, dimx); char* shortname_term = termname(); char* longname_term = longname(); @@ -826,6 +835,10 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(!opts){ opts = &defaultopts; } + if(opts->margin_t < 0 || opts->margin_b < 0 || opts->margin_l < 0 || opts->margin_r < 0){ + fprintf(stderr, "Provided an illegal negative margin, refusing to start\n"); + return NULL; + } const char* encoding = nl_langinfo(CODESET); if(encoding == NULL || (strcmp(encoding, "ANSI_X3.4-1968") && strcmp(encoding, "UTF-8"))){ fprintf(stderr, "Encoding (\"%s\") was neither ANSI_X3.4-1968 nor UTF-8, refusing to start\n", @@ -837,6 +850,10 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(ret == NULL){ return ret; } + ret->margin_t = opts->margin_t; + ret->margin_b = opts->margin_b; + ret->margin_l = opts->margin_l; + ret->margin_r = opts->margin_r; ret->stats.fbbytes = 0; ret->stashstats.fbbytes = 0; reset_stats(&ret->stats); @@ -921,9 +938,9 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ if(!opts->suppress_banner){ char prefixbuf[BPREFIXSTRLEN + 1]; term_fg_palindex(ret, ret->ttyfp, ret->colors <= 256 ? 50 % ret->colors : 0x20e080); - fprintf(ret->ttyfp, "\n notcurses %s by nick black et al", notcurses_version()); + printf("\n notcurses %s by nick black et al", notcurses_version()); term_fg_palindex(ret, ret->ttyfp, ret->colors <= 256 ? 12 % ret->colors : 0x2080e0); - fprintf(ret->ttyfp, "\n %d rows, %d columns (%sB), %d colors (%s)\n" + printf("\n %d rows, %d columns (%sB), %d colors (%s)\n" " compiled with gcc-%s\n" " terminfo from %s\n", ret->stdscr->leny, ret->stdscr->lenx, @@ -931,25 +948,26 @@ notcurses* notcurses_init(const notcurses_options* opts, FILE* outfp){ ret->colors, ret->RGBflag ? "direct" : "palette", __VERSION__, curses_version()); #ifdef USE_FFMPEG - fprintf(ret->ttyfp, " avformat %u.%u.%u\n avutil %u.%u.%u\n swscale %u.%u.%u\n", + printf(" avformat %u.%u.%u\n avutil %u.%u.%u\n swscale %u.%u.%u\n", LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO, LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO, LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO); + fflush(stdout); #else term_fg_palindex(ret, ret->ttyfp, ret->colors <= 88 ? 1 % ret->colors : 0xcb); - fprintf(ret->ttyfp, "\n Warning! Notcurses was built without ffmpeg support\n"); + fprintf(stderr, "\n Warning! Notcurses was built without ffmpeg support\n"); #endif term_fg_palindex(ret, ret->ttyfp, ret->colors <= 88 ? 1 % ret->colors : 0xcb); if(!ret->RGBflag){ // FIXME - fprintf(ret->ttyfp, "\n Warning! Colors subject to https://github.com/dankamongmen/notcurses/issues/4"); - fprintf(ret->ttyfp, "\n Specify a (correct) DirectColor TERM, or COLORTERM=24bit.\n"); + fprintf(stderr, "\n Warning! Colors subject to https://github.com/dankamongmen/notcurses/issues/4"); + fprintf(stderr, "\n Specify a (correct) TrueColor TERM, or COLORTERM=24bit.\n"); }else{ if(!ret->CCCflag){ - fprintf(ret->ttyfp, "\n Warning! Advertised DirectColor but no 'ccc' flag\n"); + fprintf(stderr, "\n Warning! Advertised TrueColor but no 'ccc' flag\n"); } } - if(strcmp(encoding, "UTF-8")){ - fprintf(ret->ttyfp, "\n Warning! Encoding is not UTF-8.\n"); + if(strcmp(encoding, "UTF-8")){ // it definitely exists, but could be ASCII + fprintf(stderr, "\n Warning! Encoding is not UTF-8.\n"); } } // flush on the switch to alternate screen, lest initial output be swept away diff --git a/src/lib/render.c b/src/lib/render.c index 81c7a6241..edd2761bb 100644 --- a/src/lib/render.c +++ b/src/lib/render.c @@ -212,8 +212,8 @@ paint(notcurses* nc, ncplane* p, struct crender* rvec, cell* fb){ // don't use ncplane_dim_yx()/ncplane_yx() here, lest we deadlock dimy = p->leny; dimx = p->lenx; - offy = p->absy; - offx = p->absx; + offy = p->absy - nc->stdscr->absy; + offx = p->absx - nc->stdscr->absx; //fprintf(stderr, "PLANE %p %d %d %d %d %d %d\n", p, dimy, dimx, offy, offx, nc->stdscr->leny, nc->stdscr->lenx); // skip content above or to the left of the physical screen int starty, startx; @@ -764,19 +764,23 @@ notcurses_rasterize(notcurses* nc, const struct crender* rvec){ // we only need to emit a coordinate if it was damaged. the damagemap is a // bit per coordinate, rows by rows, column by column within a row, with the // MSB being the first coordinate. - size_t damageidx = 0; // don't write a clearscreen. we only update things that have been changed. // we explicitly move the cursor at the beginning of each output line, so no // need to home it expliticly. update_palette(nc, out); - for(y = 0 ; y < nc->stdscr->leny ; ++y){ + // FIXME need to track outer{x,y} (position on screen) and inner{x,y} + // (position within lastframe/rvec, which is lfdimx-sized) + for(y = nc->stdscr->absy ; y < nc->stdscr->leny + nc->stdscr->absy ; ++y){ + const int innery = y - nc->stdscr->absy; // how many characters have we elided? it's not worthwhile to invoke a // cursor movement with cup if we only elided one or two. set to INT_MAX // whenever we're on a new line. leave room to avoid overflow. int needmove = INT_MAX - nc->stdscr->lenx; - for(x = 0 ; x < nc->stdscr->lenx ; ++x){ + for(x = nc->stdscr->absx ; x < nc->stdscr->lenx + nc->stdscr->absx ; ++x){ + const int innerx = x - nc->stdscr->absx; + const size_t damageidx = innery * nc->lfdimx + innerx; unsigned r, g, b, br, bg, bb, palfg, palbg; - const cell* srccell = &nc->lastframe[y * nc->lfdimx + x]; + const cell* srccell = &nc->lastframe[innery * nc->lfdimx + innerx]; // cell c; // memcpy(c, srccell, sizeof(*c)); // unsafe copy of gcluster //fprintf(stderr, "COPYING: %d from %p\n", c->gcluster, &nc->pool); @@ -907,9 +911,8 @@ fprintf(stderr, "RAST %u [%s] to %d/%d\n", srccell->gcluster, egcpool_extended_g } if(cell_double_wide_p(srccell)){ ++x; - ++damageidx; } - ++damageidx; +//fprintf(stderr, "damageidx: %ld\n", damageidx); } } ret |= fflush(out);