diff --git a/include/notcurses.h b/include/notcurses.h index 57862a596..2766db054 100644 --- a/include/notcurses.h +++ b/include/notcurses.h @@ -220,8 +220,10 @@ int ncplane_bg_rgb8(struct ncplane* n, int r, int g, int b); // Fine details about terminal -// FIXME bools for the various WA_* attributes -// FIXME verify that they are both supported AND not part of no_color_video +// Returns a 16-bit bitmask in the LSBs of supported NCURSES-style attributes +// (WA_UNDERLINE, WA_BOLD, etc.) The attribute is only indicated as supported +// if the terminal can support it together with color. +unsigned notcurses_supported_styles(const struct notcurses* nc); // Returns the number of colors supported by the palette, or 0 if there is no // palette (DirectColor or no colors). diff --git a/src/lib/notcurses.c b/src/lib/notcurses.c index d6bb9a1e3..de80722f5 100644 --- a/src/lib/notcurses.c +++ b/src/lib/notcurses.c @@ -53,6 +53,13 @@ typedef struct notcurses { char* rmcup; // restore primary mode char* setaf; // set foreground color (ANSI) char* setab; // set background color (ANSI) + char* standout; // WA_STANDOUT + char* uline; // WA_UNDERLINK + char* reverse; // WA_REVERSE + char* blink; // WA_BLINK + char* dim; // WA_DIM + char* bold; // WA_BOLD + char* italics; // WA_ITALIC struct termios tpreserved; // terminal state upon entry bool RGBflag; // terminfo-reported "RGB" flag for 24bpc directcolor ncplane* top; // the contents of our topmost plane (initially entire screen) @@ -234,6 +241,74 @@ const ncplane* notcurses_stdplane_const(const notcurses* nc){ return nc->stdscr; } +static int +interrogate_terminfo(notcurses* nc, const notcurses_options* opts){ + char* longname_term = longname(); + fprintf(stderr, "Term: %s\n", longname_term ? longname_term : "?"); + nc->RGBflag = tigetflag("RGB") == 1; + if((nc->colors = tigetnum("colors")) <= 0){ + fprintf(stderr, "This terminal doesn't appear to support colors\n"); + return -1; + }else if(nc->RGBflag && (unsigned)nc->colors < (1u << 23u)){ + fprintf(stderr, "Warning: advertised RGB flag but only %d colors\n", + nc->colors); + }else{ + printf("Colors: %d (%s)\n", nc->colors, nc->RGBflag ? "direct" : "palette"); + } + const char** cap; + for(cap = required_caps ; *cap ; ++cap){ + if(term_verify_seq(NULL, *cap)){ + fprintf(stderr, "Capability not defined for terminal: %s\n", *cap); + return -1; + } + } + term_verify_seq(&nc->standout, "smso"); + term_verify_seq(&nc->uline, "smul"); + term_verify_seq(&nc->reverse, "reverse"); + term_verify_seq(&nc->blink, "blink"); + term_verify_seq(&nc->dim, "dim"); + term_verify_seq(&nc->bold, "bold"); + term_verify_seq(&nc->italics, "sitm"); + // Some terminals cannot combine certain styles with colors. Don't advertise + // support for the style in that case. + int nocolor_stylemask = tigetnum("ncv"); + if(nocolor_stylemask > 0){ + if(nocolor_stylemask & WA_STANDOUT){ + nc->standout = NULL; + } + if(nocolor_stylemask & WA_UNDERLINE){ + nc->uline = NULL; + } + if(nocolor_stylemask & WA_REVERSE){ + nc->reverse = NULL; + } + if(nocolor_stylemask & WA_BLINK){ + nc->blink = NULL; + } + if(nocolor_stylemask & WA_DIM){ + nc->dim = NULL; + } + if(nocolor_stylemask & WA_BOLD){ + nc->bold = NULL; + } + if(nocolor_stylemask & WA_ITALIC){ + nc->italics = NULL; + } + } + // Not all terminals support setting the fore/background independently + term_verify_seq(&nc->setaf, "setaf"); + term_verify_seq(&nc->setab, "setab"); + // Neither of these is supported on e.g. the "linux" virtual console. + if(!opts->inhibit_alternate_screen){ + term_verify_seq(&nc->smcup, "smcup"); + term_verify_seq(&nc->rmcup, "rmcup"); + }else{ + nc->smcup = nc->rmcup = NULL; + } + nc->top = nc->stdscr = NULL; + return 0; +} + // FIXME should probably register a SIGWINCH handler here // FIXME install other sighandlers to clean things up notcurses* notcurses_init(const notcurses_options* opts){ @@ -267,37 +342,9 @@ notcurses* notcurses_init(const notcurses_options* opts){ fprintf(stderr, "Terminfo error %d (see terminfo(3ncurses))\n", termerr); goto err; } - char* longname_term = longname(); - fprintf(stderr, "Term: %s\n", longname_term ? longname_term : "?"); - ret->RGBflag = tigetflag("RGB") == 1; - if((ret->colors = tigetnum("colors")) <= 0){ - fprintf(stderr, "This terminal doesn't appear to support colors\n"); + if(interrogate_terminfo(ret, opts)){ goto err; - }else if(ret->RGBflag && (unsigned)ret->colors < (1u << 23u)){ - fprintf(stderr, "Warning: advertised RGB flag but only %d colors\n", - ret->colors); - }else{ - printf("Colors: %d (%s)\n", ret->colors, - ret->RGBflag ? "direct" : "palette"); - } - const char** cap; - for(cap = required_caps ; *cap ; ++cap){ - if(term_verify_seq(NULL, *cap)){ - fprintf(stderr, "Capability not defined for terminal: %s\n", *cap); - goto err; - } - } - // Not all terminals support setting the fore/background independently - term_verify_seq(&ret->setaf, "setaf"); - term_verify_seq(&ret->setab, "setab"); - // Neither of these is supported on e.g. the "linux" virtual console. - if(!opts->inhibit_alternate_screen){ - term_verify_seq(&ret->smcup, "smcup"); - term_verify_seq(&ret->rmcup, "rmcup"); - }else{ - ret->smcup = ret->rmcup = NULL; } - ret->top = ret->stdscr = NULL; if(alloc_stdscr(ret) == NULL){ goto err; } @@ -483,7 +530,7 @@ void ncplane_cursor_yx(const ncplane* n, int* y, int* x){ } static void -advance_cursor(struct ncplane* n){ +advance_cursor(ncplane* n){ if(++n->x == n->lenx){ n->x = 0; if(++n->y == n->leny){ @@ -492,7 +539,7 @@ advance_cursor(struct ncplane* n){ } } -int ncplane_putwc(struct ncplane* n, const cell* c){ +int ncplane_putwc(ncplane* n, const cell* c){ cell* targ = &n->fb[fbcellidx(n, n->y, n->x)]; memcpy(targ, c, sizeof(*c)); advance_cursor(n); @@ -516,7 +563,7 @@ int load_cell(cell* c, const wchar_t* wstr){ return copied; } -int ncplane_putwstr(struct ncplane* n, const wchar_t* wstr){ +int ncplane_putwstr(ncplane* n, const wchar_t* wstr){ int ret = 0; // FIXME speed up this blissfully naive solution cell c; @@ -533,3 +580,15 @@ int ncplane_putwstr(struct ncplane* n, const wchar_t* wstr){ } return ret; } + +unsigned notcurses_supported_styles(const notcurses* nc){ + unsigned styles = 0; + styles |= nc->standout ? WA_STANDOUT : 0; + styles |= nc->uline ? WA_UNDERLINE : 0; + styles |= nc->reverse ? WA_REVERSE : 0; + styles |= nc->blink ? WA_BLINK : 0; + styles |= nc->dim ? WA_DIM : 0; + styles |= nc->bold ? WA_BOLD : 0; + styles |= nc->italics ? WA_ITALIC : 0; + return styles; +} diff --git a/tests/main.h b/tests/main.h index 46bb786c6..80e941983 100644 --- a/tests/main.h +++ b/tests/main.h @@ -3,6 +3,7 @@ #include #include +#include // GTEST_SKIP only came along in GoogleTest 1.9 #ifndef GTEST_SKIP diff --git a/tests/ncplane.cpp b/tests/ncplane.cpp index 9c6737498..b3b4823c3 100644 --- a/tests/ncplane.cpp +++ b/tests/ncplane.cpp @@ -17,6 +17,7 @@ class NcplaneTest : public :: testing::Test { void TearDown() override { if(nc_){ + EXPECT_EQ(0, ncplane_fg_rgb8(n_, 255, 255, 255)); EXPECT_EQ(0, notcurses_render(nc_)); EXPECT_EQ(0, notcurses_stop(nc_)); } @@ -97,7 +98,6 @@ TEST_F(NcplaneTest, RejectBadRGB) { EXPECT_NE(0, ncplane_fg_rgb8(n_, 255, 256, 255)); EXPECT_NE(0, ncplane_fg_rgb8(n_, 255, 255, 256)); EXPECT_NE(0, ncplane_fg_rgb8(n_, 256, 256, 256)); - EXPECT_EQ(0, ncplane_fg_rgb8(n_, 255, 255, 255)); } // Verify we can emit a wide character, and it advances the cursor diff --git a/tests/notcurses.cpp b/tests/notcurses.cpp index ecd786f97..b1c7cddc5 100644 --- a/tests/notcurses.cpp +++ b/tests/notcurses.cpp @@ -48,3 +48,9 @@ TEST_F(NotcursesTest, ResizeSameSize) { EXPECT_EQ(newy, y); EXPECT_EQ(0, notcurses_stop(nc_)); } + +// we should at least have WA_STANDOUT everywhere, i should think? +TEST_F(NotcursesTest, CursesStyles) { + unsigned attrs = notcurses_supported_styles(nc_); + EXPECT_EQ(1, !!(WA_STANDOUT & attrs)); +}