2

Related questions

ncurses: init_color() has no effect (specific to PuTTY, xterm and gnome-terminal)

NCurses: Why does init_color return OK but still not set the color? (specific to xterm)

The issue

init_color is saving the color info in ncurses memory, and reporting success - but having no effect on the MacOS (Mojave 10.14.2) Terminal.app version 2.9.1 (421.1). ncurses was installed via HomeBrew:

$ brew info ncurses
ncurses: stable 6.1 (bottled) [keg-only]
Text-based UI library
https://www.gnu.org/software/ncurses/
/usr/local/Cellar/ncurses/6.1 (3,869 files, 8.3MB)
  Poured from bottle on 2018-12-31 at 21:59:36

My workaround might have to be reliance on the default ncurses color palette.

Based on one of the answers in a prior question, I reviewed the terminfo source. The relevant section is under this text:

# The AppKit Terminal.app descriptions all have names beginning with
# "nsterm".

In particular:

# For Apple_Terminal v309+, use "nsterm-256color" (or "nsterm-bce")

It seems there's no specific section for 10.14; the latest entry is for 10.13:

# reviewed Terminal.app in High Sierra (version 2.8 build 400) -TD
# Comparing with build361, little has changed, except that italics work.
# Direct-color is not supported, by the way.
#
# Improved rmso/rmul -TD
nsterm-build400|Terminal.app in OS X 10.13,
        rmso=\E[27m, rmul=\E[24m, use=xterm+sm+1006,
        use=ecma+italics, use=nsterm-build361,

# This is an alias which should always point to the "current" version
nsterm|nsterm-256color|Apple_Terminal|AppKit Terminal.app,
        use=nsterm-build400,

Upon reading that I'm unclear on whether this is expected behaviour.

Minimum reproducible example

When I run this, all asserts pass, but the default colours (rather than the edited colours) are shown for the foreground and background.

#include <assert.h>
#include <fcntl.h>
#include <ncursesw/ncurses.h>
#include <unistd.h>


int main() {
    int fdump = open("/tmp/ncurses_dump", O_WRONLY|O_CREAT|O_TRUNC, 0600);
    dup2(fdump, STDOUT_FILENO);

    assert(initscr() != NULL);
    assert(ERR != start_color());
    assert(has_colors());
    assert(can_change_color());
    assert(COLOR_PAIRS >= 256);
    assert(COLORS >= 256);

    // color 21 is blue by default
    // let's make it white
    const NCURSES_COLOR_T fore = 21;
    assert(ERR != init_color(fore, 998, 999, 1000));
    NCURSES_COLOR_T r, g, b;
    assert(ERR != color_content(fore, &r, &g, &b));
    assert(r == 998);
    assert(g == 999);
    assert(b == 1000);

    // color 195 is light blue by default
    // let's make it black
    const NCURSES_COLOR_T back = 195;
    assert(ERR != init_color(back, 0, 1, 2));
    assert(ERR != color_content(back, &r, &g, &b));
    assert(r == 0);
    assert(g == 1);
    assert(b == 2);

    // arbitrary
    const NCURSES_PAIRS_T pair = 100;
    assert(ERR != init_pair(pair, fore, back));
    NCURSES_COLOR_T fc, bc;
    assert(ERR != pair_content(pair, &fc, &bc));
    assert(fc == fore);
    assert(bc == back);

    // The pair init works, but the color init doesn't - this still outputs blue
    // on light blue
    assert(ERR != attron(COLOR_PAIR(pair)));
    assert(ERR != addch('X'));

    while (getch() != 'q');

    endwin();
    return 0;
}

The dump file then contains the following:

$ hexdump -C /tmp/ncurses_dump 
00000000  1b 5b 3f 31 30 34 39 68  1b 5b 32 32 3b 30 3b 30  |.[?1049h.[22;0;0|
00000010  74 1b 5b 31 3b 34 37 72  1b 28 42 1b 5b 6d 1b 5b  |t.[1;47r.(B.[m.[|
00000020  34 6c 1b 5b 3f 37 68 1b  5b 33 39 3b 34 39 6d 1b  |4l.[?7h.[39;49m.|
00000030  5d 34 3b 32 31 3b 72 67  62 3a 46 45 2f 46 45 2f  |]4;21;rgb:FE/FE/|
00000040  46 46 1b 5c 1b 5d 34 3b  31 39 35 3b 72 67 62 3a  |FF.\.]4;195;rgb:|
00000050  30 30 2f 30 30 2f 30 30  1b 5c 1b 5b 33 39 3b 34  |00/00/00.\.[39;4|
00000060  39 6d 1b 5b 33 37 6d 1b  5b 34 30 6d 1b 5b 48 1b  |9m.[37m.[40m.[H.|
00000070  5b 32 4a 1b 5b 33 38 3b  35 3b 32 31 6d 1b 5b 34  |[2J.[38;5;21m.[4|
00000080  38 3b 35 3b 31 39 35 6d  58 1b 28 42 1b 5b 6d 1b  |8;5;195mX.(B.[m.|
00000090  5b 33 39 3b 34 39 6d 1b  5b 33 37 6d 1b 5b 34 30  |[39;49m.[37m.[40|
000000a0  6d 1b 5b 33 38 3b 35 3b  32 31 6d 1b 5b 34 38 3b  |m.[38;5;21m.[48;|
000000b0  35 3b 31 39 35 6d 71 1b  28 42 1b 5b 6d 1b 5b 33  |5;195mq.(B.[m.[3|
000000c0  39 3b 34 39 6d 1b 5b 33  37 6d 1b 5b 34 30 6d 1b  |9;49m.[37m.[40m.|
000000d0  5b 33 39 3b 34 39 6d 0d  1b 5b 34 37 64 1b 5b 4b  |[39;49m..[47d.[K|
000000e0  1b 5b 33 39 3b 34 39 6d  1b 5d 31 30 34 07 1b 5b  |.[39;49m.]104..[|
000000f0  34 37 3b 31 48 1b 5b 3f  31 30 34 39 6c 1b 5b 32  |47;1H.[?1049l.[2|
00000100  33 3b 30 3b 30 74 0d 1b  5b 3f 31 6c 1b 3e        |3;0;0t..[?1l.>|
0000010e
Reinderien
  • 11,755
  • 5
  • 49
  • 77
  • Using `assert` to check for runtime errors and (even worse) to make calls to functions with side effects is a very bad idea. In release builds all your `assert`s *and their arguments* will be gone. – melpomene Jan 10 '19 at 22:18
  • 5
    @melpomene This is clearly not production code, and it's a cheap way to illustrate what's working in the example program. – Reinderien Jan 10 '19 at 22:19
  • It's sample code you've posted on StackOverflow. Someone somewhere will blindly copy/paste it into their codebase. – melpomene Jan 10 '19 at 22:21
  • When/Where does the code fail? – KamilCuk Jan 10 '19 at 22:29
  • @KamilCuk It doesn't fail any asserts; it just shows the wrong (default) colours. – Reinderien Jan 10 '19 at 22:30
  • 1
    If you modify your program to add something like `dup2(open("/tmp/seq", O_WRONLY|O_CREAT|O_TRUNC, 0644), STDOUT_FILENO);` before `attron` and `addch`, you'll be able to check /tmp/seq for the escape codes that ncurses tried to use. That might help you isolate the problem. – zneak Jan 10 '19 at 22:42
  • @zneak Edited. But I don't really know which escape codes I'm looking for – Reinderien Jan 10 '19 at 23:20
  • You can "read" it [like this](https://pastebin.com/e2eYtmed), starting from right before you call `attron`. My suspicion is that the terminal emulator does not understand the `\e]4` command (["Operating System Command"](https://www.xfree86.org/4.8.0/ctlseqs.html), Change Color Number). I'm not sure how you would tell that to ncurses, though. Do you export a TERM variable in your shell session? – zneak Jan 11 '19 at 00:44

2 Answers2

3

At this point, I'm pretty sure that the macOS Terminal does not support modifying color indices. You're getting the xterm escape sequence for doing so (\e]4) in your dump, but Apple's terminal is not a true xterm.

The likeliest explanation is that Apple's terminal declares itself as an xterm because it's almost compatible with xterm. If you go to Terminal's preferences → Profiles → Advanced, the first setting is "Declare terminal as", and the correct answer (as far as ncurses is concerned) is nsterm, not xterm-256color, the apparent default.

However, just like with browser user agents, declaring something unknown is sometimes worse than doing a fake declaration. It's probable that most of your other programs will know how to handle nsterm, but maybe not. For instance, when using nsterm, the bash install that ships with macOS stops recognizing the delete⌦ key and prints a ~ instead.

Ncurses is aware of nsterm. If you change your $TERM to that, the example program that you wrote no longer works: it asserts that color indices can't be changed on this terminal emulator, which is apparently correct.

It's possible to use 24-bit RGB colors on the macOS terminal using the \e[C;2;R;G;Bm command, where C is 38 for the foreground and 48 for the background, and R, G and B are numeric values in the 0...255 range. However, it appears that ncurses is not able to use this capability, for whatever reason.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • Sounds like I need to write a pull request for ncurses, then, if the support is theoretically there. – Reinderien Jan 11 '19 at 01:19
  • To be clear, there is support for setting an arbitrary RGB color, but it's possible that ncurses actually wants to change the color associated to an index, which nsterm does not let you do. Of course, you're welcome to try. – zneak Jan 11 '19 at 01:21
  • @ThomasDickey, OP says that they have installed ncurses 6.1 with brew, though. (Compiler/linker flag issues?) – zneak Jan 12 '19 at 15:30
  • I noticed that, and added an answer. There are [other issues](https://www.google.com/search?client=firefox-b-1&q=mohave+server.app) with Mohave, but that's Apple's problem. – Thomas Dickey Jan 12 '19 at 15:56
2

From the comments, you would need a terminfo entry (source) like this to use the new functionality in Mohave:

nsterm-direct|nsterm with direct color,
        use=xterm+indirect, use=nsterm,

Compile/install using the ncurses 6.1 tic which you installed with brew.

That's a starting point (Apple does make incompatible changes, as you might notice in the history of the nsterm entries). There are other details to investigate by testing (see tack and vttest for instance).

However: init_color is not much use in this scenario (i.e., direct colors), since it assumes you can reprogram the color-content of an RGB entry. You can't. What you can do is use init_extended_pair as shown in the picsmap example. Some people might find that useful (see discussion). A color pair only affects the data in ncurses; color content changes the terminal.

For 256 colors... Terminal.app doesn't have a changeable palette unless that was done recently. That's why nsterm-256color uses xterm+256setaf as a building block (it's a have-not in this realm). If you follow the links back to the definition, you will see this:

# palette is hardcoded...
xterm+256setaf|xterm 256-color (set-only),
        ccc@,
        colors#0x100, pairs#0x10000,
        initc@, op=\E[39;49m,
        setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;
              5;%p1%d%;m,
        setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5
              ;%p1%d%;m,
        setb@, setf@,
Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105