背景颜色对应该如何影响 ncurses 中后续字符的颜色?

How the background color pair should affect the color of subsequent characters in ncurses?

我能够用这个小示例代码重现我的问题:

#include <ncurses.h>

int main() {
        initscr();
        start_color();
        init_pair ( 1, COLOR_BLUE, COLOR_BLACK );
        init_pair ( 2, COLOR_YELLOW, COLOR_BLACK );
        bkgd ( (chtype) COLOR_PAIR(1) );
        attrset(COLOR_PAIR(2));
        printw("NO WAR!");
        refresh();
        getch();
        endwin();
        return 0;
}

一旦使用 gcc test.c -lncurses 编译,其中 test.c 是该代码,如果在 guix 中编译,./a.out 给出蓝色文本,如果在 guix 外部编译, 它给出了等同于黄色的 curses 文本。


我正在研究用 C 编写并使用 ncurses 的游戏代码。该代码启动颜色对,然后将背景设置为颜色对。我的问题是,当我从源代码制作游戏时,进一步的行为会有所不同,具体取决于 (1) 我是否像往常一样在 Debian 或 Fedora 中构建代码,或者 (2) 我是否使用 guix 构建代码。通常,字符颜色的进一步设置会按预期工作,并且我会在字符出现在屏幕上的地方用彩色字符覆盖背景。但是,如果我用 guix 构建它,所有其他字符都将以与背景分配给的相同颜色对绘制。 (我可以改变那对颜色,它会影响一切。)

如果我删除设置背景颜色对的部分,程序在这两种情况下都按预期工作,默认颜色对看起来是黑底白字,其他字符根据代码着色。

我在下面的行中发表了评论,删除它会有所帮助。

    /* set up colors */
    (void) start_color();
    if ( has_colors() && ( COLOR_PAIRS > 7 ) ) {
        state.options |= OPTION_HAS_COLOR;
        (void) init_pair ( 1, COLOR_GREEN, COLOR_BLACK );
        (void) init_pair ( 2, COLOR_RED , COLOR_BLACK );
        (void) init_pair ( 3, COLOR_YELLOW, COLOR_BLACK );
        (void) init_pair ( 4, COLOR_BLUE, COLOR_BLACK );
        (void) init_pair ( 5, COLOR_MAGENTA, COLOR_BLACK );
        (void) init_pair ( 6, COLOR_CYAN, COLOR_BLACK );
        (void) init_pair ( 7, COLOR_WHITE, COLOR_BLACK );
        
        /* Removing the following line helps */
        (void) bkgd ( (chtype) COLOR_PAIR(WHITE) );

        state.items[ROBOT].color = WHITE;
        state.items[KITTEN].color = randcolor();
        for ( i = BOGUS; i < state.num_items; i++ ) {
            state.items[i].color = randcolor();
        }
    } else {
        state.options &= ~ OPTION_HAS_COLOR;
    }
}

/*@-globstate@*/
static void draw ( const screen_object *o ) {
    attr_t new;

    /*@-nullpass@*/
    assert ( curscr != NULL);
    if ( ( state.options & OPTION_HAS_COLOR ) != 0 ) {
        new = COLOR_PAIR(o->color);
        if ( o->bold ) { new |= A_BOLD; }
        if ( o->reverse ) { new |= A_REVERSE; }
        (void) attrset ( new );
    }
    (void) addch ( o->character );
    /*@+nullpass@*/
}

完整的 C 源代码:https://github.com/robotfindskitten/robotfindskitten/blob/main/src/robotfindskitten.c

我尝试使用 guix 针对 ncurses 版本 6.2.20200212 和 6.2.20210619 进行构建。我尝试制作的 Debian 版本是 6.2+20201114-2 和 6.3-2。 Fedora 上的版本是 6.2.20210508.

差异似乎不是由环境变量引起的,因为我可以在具有本地编译、存储库和同一包的 guix 版本的 Debian 上的同一终端内重现结果。另外,我可以通过修改源代码来改变背景颜色对,这样终端就可以正确识别为支持颜色了。

根据 ncurses 手册页 (man bkgd):

  • The library first compares the character, and if it matches the current character part of the background, it replaces that with the new background character.
  • The library then checks if the cell uses color, i.e., its color pair value is nonzero. If not, it simply replaces the attributes and color pair in the cell with those from the new background character.
  • If the cell uses color, and that matches the color in the current background, the library removes attributes which may have come from the current background and adds attributes from the new background. It finishes by setting the cell to use the color from the new background.
  • If the cell uses color, and that does not match the color in the current background, the library updates only the non-color attributes, first removing those which may have come from the current background, and then adding attributes from the new background.

我的理解是,出于某种原因,在 guix 情况下,新字符被认为是在背景中,并且没有按预期着色,而是继承了背景颜色对,而在通常情况下, 它们会按预期着色。

什么会影响字符颜色的设置?它是库中的错误还是实现中的变体?删除 bkgd 部分有效,但我不确定我是否应该将其作为错误报告给软件作者或针对 Guix 的 ncurses 库,或者干脆忘记它。

调用 bkgd 后,对 waddch 的后续调用将使用背景颜色,除非 waddch 调用(或 wattrset)指定了颜色。这是在 ncurses 的 render_char 函数中完成的:

static NCURSES_INLINE NCURSES_CH_T
render_char(WINDOW *win, NCURSES_CH_T ch)
/* compute a rendition of the given char correct for the current context */
{
    attr_t a = WINDOW_ATTRS(win);
    int pair = GetPair(ch);

    if (ISBLANK(ch)
    && AttrOf(ch) == A_NORMAL
    && pair == 0) {
    /* color/pair in attrs has precedence over bkgrnd */
    ch = win->_nc_bkgd;
    SetAttr(ch, a | AttrOf(win->_nc_bkgd));
    if ((pair = GET_WINDOW_PAIR(win)) == 0)
        pair = GetPair(win->_nc_bkgd);
    SetPair(ch, pair);
    } else {
    /* color in attrs has precedence over bkgrnd */
    a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a);
    /* color in ch has precedence */
    if (pair == 0) {
        if ((pair = GET_WINDOW_PAIR(win)) == 0)
        pair = GetPair(win->_nc_bkgd);
    }
    AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
    SetPair(ch, pair);
    }

    TR(TRACE_VIRTPUT,
       ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)",
    _tracech_t2(1, CHREF(win->_nc_bkgd)),
    GetPair(win->_nc_bkgd),
    _traceattr(WINDOW_ATTRS(win)),
    GET_WINDOW_PAIR(win),
    _tracech_t2(3, CHREF(ch)),
    GetPair(ch)));

    return (ch);
}

但是,在 guix 2.0 报告的 March 2020 邮件列表中讨论了 ncurses 6.2 中的一个错误。呼叫者有一个简单的解决方法(可能会影响某些用户)。问题中没有说明guix的版本。